Proposal: Struct Delegates #7785
Replies: 5 comments 18 replies
-
Why isn't the |
Beta Was this translation helpful? Give feedback.
-
Similar to #1060 |
Beta Was this translation helpful? Give feedback.
-
A delegate to a static member function would have |
Beta Was this translation helpful? Give feedback.
-
Regarding the static method discussion - why not just do: public readonly unsafe struct MyValueDelegate<T> : IEquatable<MyValueDelegate<T>>
{
private readonly object? _instance;
private readonly IntPtr _mPtr; // it's just a method pointer, can we just put it in an IntPtr?
// Possibly hidden in C# the same way regular delegate constructor is hidden.
public MyValueDelegate(object? instance, IntPtr mPtr)
{
_instance = instance;
_mPtr = mPtr;
}
public void Invoke(ref T arg)
{
if(_instance != null)
((delegate*<this object, ref T, void>)_mPtr)(_instance, ref arg);
else
((delegate*<ref T, void>)_mPtr)(ref arg);
}
public bool IsValid => _mPtr != IntPtr.Zero;
} |
Beta Was this translation helpful? Give feedback.
-
Completely concur with this proposal! Today I'm using similarly function pointers in lots of performance critical code to avoid the usage of delegates, but it comes with a painful experience (unsafe, explicit cast for fp...etc). |
Beta Was this translation helpful? Give feedback.
-
Struct Delegates
Delegates, but with all the cruft trimmed away.
Today, we can declare whatever kind of delegate we want using this syntax:
And that essentially generates a class that implements
MulticastDelegate
.This has lots of overhead with it, including the
object
overhead, multicast overhead,BeginInvoke
overhead.With Struct delegates we only use the bare minimum required to invoke (a struct containing an
object
instance and a pointer to an instance method).Declaration syntax is just like with regular delegates, but with the
struct
keyword prepended.And that generates a much simpler struct
This type does not support multicast or Begin/EndInvoke.
With compiler support, constructing a struct delegate can be done in all the same ways as constructing regular delegates, using method groups or lambdas. Capturing vars or using a method group to a static function will result in the appropriate classes being generated the same way they are today. The only difference is the delegate itself is a 2-word-sized struct instead of a large class.
This results in zero allocations (not counting lambda captures) and super fast invoke.
Caveats
Because it's a struct,
action?.Invoke()
currently doesn't work the same way it does on regular delegates. It would be nice to have first-class language support where that is allowed and is simply lowered to a check toIsValid
. But I'm not holding my breath.Because it's a struct instead of a class, it could be torn if 2 threads read/write the same field. I think that shouldn't be a blocker, and could be solved by an Atomic<T> type
What about other allocation-free callback proposals?
There have been other proposals like #3452 and #1060. The difference is those proposals are using variable-sized generic structs, while this uses a fixed-sized struct. The advantage to those other proposals is even capturing variables can be alloc-free, while this would have to allocate a display class. The disadvantage is multiple callbacks cannot be laid out contiguously in memory like this fixed-size struct can. This makes it ideal for implementing a high-performance event library (not like C#'s events).
Also, struct delegates allow algorithms to cache the pointer to an interface method for improved performance without changing the API.
They both have their advantages and disadvantages and can co-exist.
--
Isn't this like Static Delegates?
No, that was for invoking static methods and was superseded by function pointers. This is for invoking instance methods the same way as regular delegates.
Alternatives
Rather than using
struct delegate
syntax to declare a delegate type, what about adding first-class support for arbitrary delegates? Similar to #5321, we could just writedelegate<int, ref object, void>
without explicitly declaring thestruct delegate
. This may be too large of an ask, but it would feel very natural imo.--
Create your own struct delegate using existing function pointer support.
I have done exactly this. And while it does work, it is quite clunky.
That's way more verbose than
And it requires unsafe code. Also, because it's invoking through a static function (because C# currently doesn't support instance function pointers #7331), it's slightly slower than it could be.
Beta Was this translation helpful? Give feedback.
All reactions