Proposal: Skeleton Classes #8742
Replies: 6 comments
-
This is an interesting proposal, though I do have comments:
|
Beta Was this translation helpful? Give feedback.
-
Thanks for the feedback. Definitely all worthwhile discussions points. I was not keen on the naming while I wrote it and that's why there's a proposition in the bottom to drop the On point 2, I think analyzers can provide the bulk of what we'd need for compile-time safety. I'm not sure you can truly solve this in the compiler without taking the proposal into the realms of code generation. Do you think this would be a blocker? I see so many uses for this feature I don't see it being a problem for me at least. Analyzers seem like a very low-cost solution to this problem. In my mind this was always more of a dynamic/strong bridge anyway and I expected the dynamic part to show itself. On that note, you've given me an idea that might make this proposal more palatable. On point 3, I wish so much that we'd see some code generation techniques in the language! Judging by the discussion around here I don't think this is happening any time soon. However, I take the point and perhaps separating this feature more clearly would give it a better chance of being understood on its own merit. So, on to this thought... If we re-frame this proposal as a way to wrap "dynamic" things with "strong" things, i.e. a way to improve the type safety of your code, thus moving it firmly out of the code generation bucket and into dynamic land, does that make it more appealing? It can still be used for the purposes outlined in my motiviation section. It would be the opposite of DynamicObject, let's call it a "strong" object, and it would look like this: I type this: public dynamic class MyClass : ImmutableBase
{
public string Name { get; }
public DateTimeOffset DateOfBirth { get; }
public string WithName(string name);
public DateTimeOffset WithDateOfBirth(DateTimeOffset dateOfBirth);
} And the compiler does this: public class MyClass : ImmutableBase
{
protected dynamic Implementor; // (this is actually provided by the base, shown here for clarity)
public string Name => Implementor.Name;
public DateTimeOffset DateOfBirth => Implementor.DateOfBirth;
public string WithName(string name) => Implementor.WithName(name);
public DateTimeOffset WithDateOfBirth(DateTimeOffset dateOfBirth) => Implementor.WithDateOfBirth(dateOfBirth);
} Thus we have something that maps something dynamic back to something strong, and all the good stuff that comes with being strong. Implementors (i.e. the person authoring ImmutableBase) can then use existing dynamic functionality (e.g. DynamicObject) to provide the dynamic part of the implementation and publish it via the protected Implementor field. While this serves as a better window into the proposition, I would still recommend using something more like the GetXImplementation approach detailed in my original proposal (for performance reasons). What are your thoughts? |
Beta Was this translation helpful? Give feedback.
-
Also, I wonder if my proposal above might be confusing people. I'm not sure I've explained the concept as well as I could have. If there's anybody here that has managed to digest what I'm blathering on about, likes the idea, and has some ideas for making the proposal more accessible - please let me know! |
Beta Was this translation helpful? Give feedback.
-
This is gonna be available with "Wither-methods" aka Caller-receiver parameters outlined here. Then it'll just boil down to: public MyClass With(string name = this.Name, DateTimeOffset dateOfBirth = this.DateOfBirth);
//usage:
var my2 = my1.With(name: "Joe"); Record types will automatically generate these methods for you, and are immutable by default, so your whole proposal is rather redundant. (Not to mention your object design; using |
Beta Was this translation helpful? Give feedback.
-
Hey joe, thanks for the link. I've seen that proposal and I agree that the functionality is redundant in that case. I used this example because people already understand it but perhaps that was my mistake because it seems to be leading you to believe I am solving an already-solved problem. The implications for what I'm suggesting are huge and it is definitely not redundant! I've been digging around and uncovered this excellent post: #341 which provides a rundown of the kind of problems I'm trying to solve here. As you can see it's quite significant. I don't think the dynamic approach solves everything on there, and definitely not in an "ideal" way. But in a practical sense it is potentially far more achievable than some of the other proposals being discussed (e.g. code generators) and even something I could contribute myself. The implementation could be small enough and completely non-breaking so as to be able to fit into a point release. Re: using |
Beta Was this translation helpful? Give feedback.
-
I was thinking about this over weekend. I'm not keen on any of the syntax I've proposed so far. I think this would be better with one of these options: 1. A new keyword. public skeleton class MyClass : ImmutableBase
{
public string Name { get; }
public DateTimeOffset DateOfBirth { get; }
public string WithName(string name);
public DateTimeOffset WithDateOfBirth(DateTimeOffset dateOfBirth);
} 2. Just a base class. public class ImmutableBase : Skeleton // The base class is a special compiler-recognised type
{
// NB. The implementation of this is the same as detailed in the proposal.
protected static Getter<THost, TValue> GetPropertyGetImplementation<THost, TValue>(PropertyInfo propertyInfo) ...
protected static Setter<THost, TValue> GetPropertySetImplementation<THost, TValue>(PropertyInfo propertyInfo) ...
// etc...
}
public class MyModel : ImmutableBase // no keywords needed, skeleton inferred
{
public string Name { get; }
public DateTimeOffset DateOfBirth { get; }
public string WithName(string name);
public DateTimeOffset WithDateOfBirth(DateTimeOffset dateOfBirth);
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi everyone!
A proposal for your consideration. A simple but effective way to declare bodyless properties, methods etc. and have them implemented with a standard pattern. I am very keen to see some way of reducing common boilerplate code in C# and would really welcome your thoughts on the approach I have detailed here:
https://github.com/rossdempster/csharplang/blob/master/proposals/skeleton-classes.md
I have not published a prototype yet, but I have been getting myself familiar with the inner workings of the compiler and I am happy to spend the time if there is enough interest in the feature.
Thanks all,
Ross.
Beta Was this translation helpful? Give feedback.
All reactions