Replies: 6 comments
-
Delegates are the mechanism for function/method pointers on the CLR and they've been there since at least the earliest public alphas of both the language and the runtime. So they weren't added as some kind of backfill for a missing feature. They are the feature. The only other method for invoking function pointers is Much of what you've asked for has been requested in other proposals here. None are so easily implemented because at the end of the day they all need to boil down to delegates, from which most of the limitations lie. Function literals still need to become delegate types. Signature equivalence will still incur a double-invocation penalty (and it's already trivial to "cast" between delegate types by making a new delegate to the original delegate's Allowing for |
Beta Was this translation helpful? Give feedback.
-
"Function Types" being nominal instead of structural is baked into the CLR. As long as that is true it is hard for the language to work around. At this point it isn't clear it would be valuable to work around it. |
Beta Was this translation helpful? Give feedback.
-
Hi HaloFor,
thank you for the clarification. I retired my comment, since I feel it is not as well founded as I thought first. I’m looking at things from a language user level. I understand now that delegates are the feature, but they don’t feel very natural to me – as I wrote, they feel more like a workaround which they are not. I see that in your use case with generic functions, they make much sense as a base class which, in a way, looks generic as it abstracts away concrete signatures. On the other side, Albahari and Albahari say “A delegate is an object that knows how to call a function” which is different from saying “A delegate is a function or a set of functions which can be called.” The former is technically correct, the latter reflects the way I’m thinking when, for instance, I’m programming in JavaScript – of course, without multicast functions (although it is not very difficult to implement something similar in JavaScript). The ease of working with functions in JavaScript is something I’d like to feel with C# as well. At the same time, I don’t want to miss all the features I had come to love with C# - I’m well aware of the fact that C# and JavaScript are two very different beasts. I see considerable effort being done by many people in developing programming languages in order to make developers more productive, which in particular holds true for C#. I highly appreciate this.
If you don’t mind, I would like to ask you another question. When I’m giving lectures on programming, I often have to talk about exception handling which is a beautiful mechanism to deal with irregular situations. However, this comes at some cost in processing resources. So, checking result codes is still an option, but not the old C-way which used to handle this with “impossible” return values. In COM-programming, we had HResult, in .Net, sometimes we have method pairs such as Int32.Parse() and Int32.TryParse(). Go, as a programming language, returns pairs of values, which lets me think at callback functions which frequently receive a pair of values, as in many JavaScript frameworks and libraries, e.g. Promises, where the first is parameter usually is an error object and the second a data object. Something like this can be realized in C# with generic 2-field structs (RetVal<T>) as containers for return values where one field holds data or a reference to a data object, and the second a reference to an exception object which is null if and only if no error condition occurred. You can easily define a cast of this struct to the type of its data type which throws automatically if the exception field is not null. An implementation of this is straight forward, and even possible for actions returning void. In such cases, I use a Void datatype represented by an empty struct (which actually consumes 1 byte of storage for each variable of this type). If I decide to program with var variables receiving return values, then the caller is responsible to check the result since the compiler would assign a RetVal type to the variable. Declaring an explicit type or converting explicitly to the type of the result field means opting for exception handling – instead of defining a DoSomething() and a TryDoSomething() method, I end up with one method only. However, I do not use this in real projects, I’m just exploring possibilities. Also, I did not arrive at a conclusion whether this is really a useful approach or not, but the least I can say: It’s definitely worth to think about!
Now I found that casting is allowed only for implicit or explicit assignment operations, so I cannot cast without declaring a variable to hold the result of the function call. In cases where the (regular) return value would be ignored, this is a (not very significant) code smell. Do you know if this is an artificial restriction imposed by the compiler, or is the reason the CLR because it is the way it is? By the way, it is this exploriation why I mentioned that a type void without values could have interesting applications. Here it would be a natural replacement for my Void data type. A field of type void cannot hold any value so it should always be considered initialized. A constructor of RetVal<void> which had the structure { void value; Exception e; } needed to initialize value by a non-value which could be formally expressed by value = default(void); This is exactly what my implementation does with the one-argument constructor which takes an exception as argument – of course, void needs to be replaced with Void in my implementation. The void case should be handled by the compiler in a way so the CLR won’t complain. I don’t know if this is possible. Similarly, could the compiler handle an explicit conversion to void simply by discarding the RetVal<void> value? Finally, converting a type T value to a RetVal<T> value is straightforward, simply use the constructor of RetVal<T> with a second argument of null. However, binding T to void would create a constructor with a parameter incapable of holding a value. Maybe this is easy to implement: If the type T is void the compiler could simply skip the step where value would be initialized normally. Again, I have no idea how the compiler actually would deal with this.
Kind regards,
Goetz
Von: HaloFour [mailto:notifications@github.com]
Gesendet: Sonntag, 11. Februar 2018 01:48
An: dotnet/csharplang <csharplang@noreply.github.com>
Cc: hellerim <heller@hellerim.de>; Mention <mention@noreply.github.com>
Betreff: Re: [dotnet/csharplang] Proposal: Support System.Delegate as a generic constraint (#86)
@hellerim <https://github.com/hellerim>
Delegates are the mechanism for function/method pointers on the CLR and they've been there since at least the earliest public alphas of both the language and the runtime. So they weren't added as some kind of backfill for a missing feature. They are the feature. The only other method for invoking function pointers is ldftn/calli which is unsafe and unverified in all but the strictest circumstances.
Much of what you've asked for has been requested in other proposals here. None are so easily implemented because at the end of the day they all need to boil down to delegates, from which most of the limitations lie. Function literals still need to become delegate types. Signature equivalence will still incur a double-invocation penalty (and it's already trivial to "cast" between delegate types by making a new delegate to the original delegate's Invoke method). Treating void like unit could only go so far as syntax candy and wouldn't improve the situation with generics given that void requires a different IL stream than literally any other type.
Allowing for System.Delegate as a generic type parameter constraint is a much easier job, especially since the CLR has always supported it and C# already supports enforcing the constraint when a type or method has such a constraint. It's a question of lifting the imposed limitation and updating the spec, docs and tools accordingly.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <https://github.com/dotnet/csharplang/issues/86#issuecomment-364708977> , or mute the <https://github.com/notifications/unsubscribe-auth/AA2ldF9JYAMjukoHfRzrZ9_cF_Kd8oPqks5tTjjKgaJpZM4MBEPz> thread. <https://github.com/notifications/beacon/AA2ldDTl0yugLinEE-FHK4g9hq3OdmAcks5tTjjKgaJpZM4MBEPz.gif>
|
Beta Was this translation helpful? Give feedback.
-
^ Estimated reading time: 4m32s. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour |
Beta Was this translation helpful? Give feedback.
-
Assigning back to champion as feature is now complete. |
Beta Was this translation helpful? Give feedback.
-
Copied from dotnet/roslyn#158
Problem
In C# it is currently not legal to specify that a generic type parameter be constrained to
System.Delegate
.This is a limitation imposed only by the C# compiler. The .NET runtime fully supports generic type constraints of System.Delegate and the C# compiler does support consuming said constraints from assemblies that have been compiled from languages without that limitation:
Proposal:
Remove the artificial limitation and allow a generic type constraint to be of type
System.Delegate
. Since this syntax is currently illegal in C# doing so would not impact any existing code.Note that I am not suggesting to support the keyword
delegate
. My opinion is that the keyword be left unsupported for potential future constraint work that would allow specifying a required signature for the delegate which would expose the ability to invoke the delegate. The runtime does not currently support such a notion and it would be unenforceable.Use Cases
Type-safe helper methods to combine delegates:
Helper methods to subscribe/unsubscribe event fields:
Beta Was this translation helpful? Give feedback.
All reactions