Replies: 34 comments
-
OK. Perhaps leave out the public keyword too:
instead of:
I do not get public here. It's an interface. It's already public. How could you make it private? |
Beta Was this translation helpful? Give feedback.
-
Perhaps the ref keyword has a place here? I am not sure.
|
Beta Was this translation helpful? Give feedback.
-
Is combining the field/property declaration and its delegation to the interface possible? // field
private readonly explicit IAnimationPlayer => animationPlayer;
// property
public implicit IAnimationPlayer => TheAnimationPlayer { get; set; }
|
Beta Was this translation helpful? Give feedback.
-
Is the // field
private readonly explicit IAnimationPlayer animationPlayer;
// property
public implicit IAnimationPlayer TheAnimationPlayer { get; set; }
|
Beta Was this translation helpful? Give feedback.
-
One concern I have with combining the declaration and delegation is that the class implementor might (for internal reasons) want to use a more specific object than the interface being publically implemented. For example, extending the above case we add a new AnimationPlayer subclass which exposes additional features that Actor depends on:
Of course, Actor could have a property that wraps a pure IAnimationPlayer and does the casting automatically, but that negates the conciseness gained from having the interface delegation in the first place. |
Beta Was this translation helpful? Give feedback.
-
Still 'animationPlayer', the interface delegating field, points to a specific class
error: double declaration with
What field of the double declaration do you want to address? Nevertheless public interface IBlendable {
public UseBlending {get; set; }
}
public class BlendingAnimationPlayer :
AnimationPlayer,
IBlendable
{
public bool UseBlending { get; set; }
/***/
}
public class Actor : IAnimationPlayer, IOtherActorInterface
{
public implicit IAnimationPlayer animationPlayer;
public implicit IBlendable blendableAnimationPlayer;
public Actor(BlendingAnimationPlayer animationPlayer)
=> (this.animationPlayer, blendableAnimationPlayer)
= (animationPlayer, animationPlayer);
/***/
} |
Beta Was this translation helpful? Give feedback.
-
In the extended case I listed above, both the field and the constructor parameter are of the derived type BlendedAnimationPlayer. So the case I'm trying to illustrate: the class that delegates interface implementation to a field, is internally using a field that is of the derived type, not just of the interface type.
I don't follow how this is an error? I'm reverting to the separate delegation syntax I first suggested, explicitly specifying the field so that a type other than the implemented interface can be used.
This is (like the rest of the example :) ) somewhat contrived. I just used this as an example where the delegating class (Actor) would want to use functionality on the specific implementing class it uses (BlendingAnimationPlayer) that does not exist in the interface it wishes to delegate (IAnimationPlayer). Yes the user absolutely could define an addtional interface, but it seems to me that shouldn't be necessary just to be able to use the language feature. In short it seems to me that limiting the type of that field or property to the interface type being implemented, imposes unnecessary restrictions that limit the usefulness of the feature. |
Beta Was this translation helpful? Give feedback.
-
First, i want to cite myself (#500 (comment)):
I suggest renaming this "Feature Request" to "Proposal". Then contrived code is no issue in my eyes. Your sample is kind of real world already.
My bad 😊 I get your point. But imagine you have two objects. public class Foo : IBar
{
object obj1;
object obj2;
implicit IBar => obj1;
public void SwapInterfaceReference()
=> {{{ ehm, how do I assing obj2 to the interface now? }}}
} This can be solved with a field of type Yet I am still not conviced of the We could combine the two private readonly implicit BlendingAnimationPlayer animationPlayer : IAnimationPlayer;
// or alternative suggestion
private readonly BlendingAnimationPlayer animationPlayer : implicit IAnimationPlayer;
// and
private readonly implicit IAnimationPlayer animationPlayerInterfaceDelegator; // field |
Beta Was this translation helpful? Give feedback.
-
To be clear, when I say the example is contrived, that does not mean it is not based on a very real example we have of this need. The solution I currently mainly work on (and past ones as well) makes extensive use of this pattern. For brevity and code privacy reasons I preferred not to dump a full real example here. If you'd like access to one though, we can discuss in PM :) This is absolutely a feature request in the sense that it would make a concrete improvement to our real codebase. Your latest iteration handles the derived typing problem beautifully. The use of the colon is definitely a significant clarity improvement. I initially suggested the => operator for its similarity to expression-bodied members. Functionally it amounts to a grouping of expression members. However the colon is probably a far clearer indication to readers that it has something to do with interface use. The latter examples with the implicit/explicit after the colon make more sense for grouping that information with the interface being implemented. |
Beta Was this translation helpful? Give feedback.
-
👍 Absolutely okay, then it is a concrete "Feature Request" 😃 Maybe just, next time, make it a proposal first and when a final syntax stands in the discussion create a new, concrete qureatures feet (ups, letters dropped 😁 😉) I like using the // field declaration
private readonly implicit IAnimationPlayer animationPlayerInterfaceDelegator;
// operator declaration
public static implicit operator double(Fraction rhs) { } On the other hand the private readonly BlendingAnimationPlayer animationPlayer : implicit IAnimationPlayer; ( I have proposed another construct #136 that makes use of the colon in a inheritance like way) That said, I will not go any longer for the syntax of: private readonly implicit IAnimationPlayer animationPlayerInterfaceDelegator; because it has the restriction that the field is stuck to a concrete interface. Let's assume you embed an interface instance, that itself implements several interfaces by interface inheritance. But you only want to expose one of those interfaces to the outside world. Then again you have to use the more detailed syntax. private readonly IBaseInterface _classInstance : explicit IDerivedInterface; In case that the type of the holding field and the implemented interface are the same, there will be type duplication. But even that won't be any different from member initializations (they cannot be private IAnimationPlayer animationPlayer : implicit IAnimationPlayer;
private List<string> _fileNames = new List<string>(); A potenial initializer always comes last. Finally, the best solution for the whole construct to me is one syntax only. private IMyInterface _classInstance : explicit IMyInterface;
protected EmbeddedClass EmbeddedInstance : implicit IMyInterface
{ get; set; } = new EmbeddedClass ();
// following would be legal syntax, but has limited use cases
private IMyInterface _classInstance : explicit IMyInterface = new ImplementigClass(); |
Beta Was this translation helpful? Give feedback.
-
I think this concept (as well as many others which propose sugar syntax to make compiler generate repetitive, standard code) belongs to Source Generators (aka replace/original) #107. |
Beta Was this translation helpful? Give feedback.
-
Source generators have the disadvantage of fragmenting the programming experience to individual, company internal behaviours instead of offerering homogeneous syntax for all users for recurring tasks. In my code there were also some "interface delegations" already. I believe that this is a really nice candidate to make available to all programmers. And imo one that many programmers will benefit from. |
Beta Was this translation helpful? Give feedback.
-
Agreed, I really don't see any problem with this.
This also has the potential use of delegating multiple implemented interfaces, should the owned object also implement those interfaces:
|
Beta Was this translation helpful? Give feedback.
-
Oh and a consequence of this proposal, which I think is mostly positive but may be considered by some to be negative, is less need for explicit maintenance in the delegating class should the interface change in some way. Currently, if an interface changes, all classes implementing the interface need to be updated accordingly. In a codebase where there are a large number of classes that simply delegate to another object through explicit methods (or even expression-bodied members), each of those classes needs to be touched, just to add more wrapper code. With this proposal, only the classes being delegated to would change. |
Beta Was this translation helpful? Give feedback.
-
But the delegating classes must still be recompiled 😉 |
Beta Was this translation helpful? Give feedback.
-
Actually @gafter, here's a more complete example that demonstrates some of the flexibility achieved through @lachbaer's suggestions:
|
Beta Was this translation helpful? Give feedback.
-
private readonly RenderableAnimationPlayer player
: implicit IAnimationPlayer, explicit IRenderable
= new RenderableAnimationPlayer(); or public RenderableAnimationPlayer player
: implicit IAnimationPlayer, explicit IRenderable
{ get; set; }
= new RenderableAnimationPlayer(); |
Beta Was this translation helpful? Give feedback.
-
I'd rather not add more complexity to that example, but yes @gafter please add these two example calls for demonstration of flexibility:
|
Beta Was this translation helpful? Give feedback.
-
I just wrote a custom struct that has many interfaces that could all be delegated: public struct NNDouble : IComparable, IComparable<double>,
IConvertible, IEquatable<double>, IFormattable
{
private double value : implicit IComparable, implicit IComparable<double>,
implicit IConvertible, implicit IEquatable<double>,
implicit IFormattable;
} Guess, how much code that could have saved me 😃 But when having this feature it would be good to have the ability to override some of the delegated methods by user code. Those should have precedence of the delegation and not produce compile time errors. |
Beta Was this translation helpful? Give feedback.
-
Yes. What would that look like? Just automated overriding of the delegated methods, so no additional decorations on the method? |
Beta Was this translation helpful? Give feedback.
-
@mattbenic Interface methods must be public. The access modifier only belongs to the field. Whether the interface is implemented explicitly or implicitly is stated by the private double value : explicit IComparable; would be synthesized as private double value;
public int IComparable.CompareTo(object obj) => value.CompareTo(obj); If however a custom |
Beta Was this translation helpful? Give feedback.
-
@gafter is there anything you're still waiting for from the participants in this thread in order to consider this feature for implementation? I'm implementing a new feature set to the same application that triggered this request, and once again I find myself wishing for a simpler way to delegate interface implementation work to a contained object :) |
Beta Was this translation helpful? Give feedback.
-
@mattbenic I don't think anyone has been interested in 'championing' this feature. Without a champion, there's nothing to move it forward through the long and involved language design process. |
Beta Was this translation helpful? Give feedback.
-
What is required of such a champion? Is there any more info on that? |
Beta Was this translation helpful? Give feedback.
-
The 'champion' is an LDM member who believes the feature is worth doing and is willing to take the feature through all the steps from proposal all the way through implementation. |
Beta Was this translation helpful? Give feedback.
-
Ah ok, so I would need to convince/bribe someone on the team then. It's not a role I could take on/work up to. :/ Thanks for the clarification 👍 |
Beta Was this translation helpful? Give feedback.
-
You could always help out by making a proposal and/or working on an impl. Other community members have done that. However, without a champion, it won't make it into the language. |
Beta Was this translation helpful? Give feedback.
-
Though this would be a neat feature to have, I meanwhile think that this is pure syntactic sugar. A code generator (e.g. taking the suggested syntax and suggesting a 'fix') that puts the whole boilerplate into a region would already do. Besides that helps when you want to make a tiny alteration to one of the interface methods, because the code for all other methods will already be there and codewise documented. |
Beta Was this translation helpful? Give feedback.
-
Agreed, it is basically syntactic sugar, as are expression bodied members. I'll put together a proposal for this when I get a chance, and link to it here for additional discussion. From my understanding of the repo's readme, I wouldn't be able to PR it until a team member requests it anyway. |
Beta Was this translation helpful? Give feedback.
-
The difference to expression bodies is that short method definitions happen all over the place and can visually shorten the code immensly. But they don't implement "big" code behind the scenes. Also they syntactically go hand in hand with lambda constructs and look very similar. This idea here is to "hide" big (auto-generated) code. It facilitates maintenance in the case that you use an interface this way a lot in many different classes and you alter the interface later in the process. But here lies the problem! When you alter the interface it shows that you have made a poor design decision before (no offense to anyone, this just happens). And when the interface is changed, a quick code review in every place where it is used might be wise. A shortcut that hides a lot of code would be absolutely counterproductive in this case. A code generator gives you the full "behind-the-scenes" code in every class what can make maintaining easier in the long haul because it may prevent semantic errors from the very start (of the interface alteration). |
Beta Was this translation helpful? Give feedback.
-
@mattbenic commented on Fri Apr 21 2017
Expression-bodied members were a great addition to C#6.
Expanding from that feature and syntax, being able to simplify the common pattern of a class implementing an interface by containing an instance of another class that implements that field would be very helpful. Similarly to with automatic properties, at compile time expand this to common properties and methods directly calling the corresponding properties and methods on the internal field.
Example:
With Actor compiling to:
Beta Was this translation helpful? Give feedback.
All reactions