I'm attempting to build a recurring programming pattern into a generic class for code re-use. Part of this pattern thread-safely subscribes/unsubscribes to a delegate as needed during asynchronous operations (multicast delegate used as event).
The following code does not compile. The problem I'm facing is that C# allows me to pass a delegate as method argument, but anywhere inside MyWorker class the subscribe +=,-= operations cause errors. (Works fine on the same delegate from outside).
[Operator '+=' cannot be applied to operands of type 'delegateT' and 'method group']
Syntactical errors?
delegate scope limitation?
is this disallowed by the language?
public class MyWorker<delegateT,argT>
{
private delegateT mDelegate;
public MyWorker(delegateT d)
{
mDelegate = d; //save delegate (reference?) for use later
}
public void DoWorkAsync()
{
mDelegate += m_subscriber; //ERROR: Operator '+=' cannot be applied to operands of type 'delegateT'...
//...Do some work that causes delegate to fire...
mDelegate -= m_subscriber; //ERROR: Operator '-=' cannot be applied to operands of type 'delegateT'...
}
private void m_subscriber(argT arg)
{
Console.WriteLine("Received: " + arg.ToString());
}
}
Note that generics do not seem to be the cause; I've tried using static types instead with the same result. I've also tried passing delegate with the 'ref' keyword to make sure i'm storing and referencing the original object rather than a local copy...maybe a secondary issue, but one step at a time.
[UPDATE]
OIC, inability to constrain Delegate type seems to be a major issue here, and prevents generic usage the way I'm doing it. Thanks for pointing that out Ron.
To clarify the original intent:
I use delegates for internal events; more flexibility than strict 'event' type. I'm searching for some way to pass a delegate (ref?) to MyWorker class at runtime. Then MyWorker performs some background tasks which subscribe and unsubscribe as necessary to receive events during operation, before finally exiting for garbage collection. I do this for 30+ nearly identical tasks/events, which is why creating a reusable (generic?) class is highly desirable.
Delegates in my system have a strict form such as:
delegate void delegateEvent1(Class1 arg);
delegate void delegateEvent2(Class2 arg);
...
delegate void delegateEventN(ClassN arg);
Plan B is to pass Action delegates to subscribe and unsubscribe from within MyWorker. This is quite a bit messier as it requires 2 Actions<> to be created for each invocation rather than just cleanly passing the desired delegate.
Open to any suggestions...
It's not because you named your generic parameter delegateT compiler knows it's some delegate type and so it can't know that some += operator exists on that type.
It would need some generic constraint to enforce this ; but AFAIK that's not possible in C#
As KiwiPiet pointed in comment you can maybe try to add a constraint for some delegate type though
As pointed by Ron Beyer in comment it's also not possible to constraint to some delegate type too
Related
class MyClass<T> {
public event T MyEvent;
}
Error: CS0066 'MyClass<T>.MyEvent': event must be of a delegate type.
Okay… C# ≥7.3 allows Delegate as base class constraint. Let's use that:
class MyClass<T> where T: Delegate {
public event T MyEvent;
}
Error: CS0066 'MyClass<T>.MyEvent': event must be of a delegate type.
WTH???
Though I can't find a documented limitation in the C# spec, I can see at least two problems with supporting such an event in C#/CLR, both related to how it is raised.
First difficulty: in the language
C# only allows raising an event from within the type that declares it. But if your generic class doesn't even know the number of parameters it T, what should the code that raises the event look like?
class MyClass<T> where T: Delegate
{
public event T MyEvent;
public void DoSomething()
{
// raise MyEvent here
MyEvent(/* what goes here? */);
}
}
Of course, you can make MyClass abstract and say that inheritors that specify the type of T would raise the event. However, this would be quite an inconsistent language design, to my opinion.
Second difficulty: in the compiler
CLR implements runtime generics. This means, that compiler must generate IL that should be good at runtime for any T that meets the generic constraints.
Raising an event is basically invoking a delegate that's stored in the event field. The compiler should generate IL that roughly includes these steps:
push delegate object reference onto the stack
push argument 1
push argument 2
....
push argument N
call delegate's Invoke method
If the delegate isn't void, an additional step is required:
pop return value from the stack and possibly store it in a field or a local variable
As you can see, the generated IL strictly depends on the number of arguments and whether the delegate is void. Therefore, such IL cannot be good for any Delegate.
In contrast
Having event delegate with generic parameters is perfectly OK, such as:
delegate void MyEventHandler<K, V>(K key, V value);
because the number of the parameters and whether the delegate is void is known at compile time. In this case the same set of IL instructions can be generated that is good for any K and V. In the IL, K and V are generated as type placeholders, which CLR is capable of resolving at runtime.
I have to add multiple methods to the invocation list of a delegate. However, all of them have decision logic associated with them. So, there's an if block before the method gets attached to the invocation list of the delegate. Can I do this without instantiating the delegate. The code snippet looks like the following:
public delegate void SomeDelegate();
static void Method1() {}
static void Method2() {}
static void AddMethodsToInvocationList()
{
SomeDelegate someDelegate = new SomeDelegate();
if (someLogic1) someDelegate += Method1;
if (someLogic2) someDelegate += Method2;
}
Basically, I wish to be able to create an instance of the delegate without passing any methods as parameters. However I get a compiler error with "does not contain a constructor that takes 0 arguments" error, if I try and instantiate the delegate without passing any methods as parameters.
I would also be open to solving this issue a different way if someone else has a better way of doing it. However, delegates have to be used.
Thank you for any help. Much appreciated.
You can simply initialize it to null. That is the idiomatic value for a multicast delegate that doesn't have any operations added to it.
Like I told you in my previous question I am learning about delegates or better said I am trying to answer all my question I have about those bad boys.
MSDN Docs somehow do not really help me much. They couldn't really answer me following issue:
I have this code:
public delegate void Del(string message);
Now what you told me yesterday is that somehow compiler creates a delegate from that line with the type Del.
You also told me yesterday that every delegate inherits from Delegate (with cap letter) class.
So far so good but now my question is I can do following Del d = DoSomething; d(); but why can't I do the same with Delegate?.
I cannot do something like this Delegate e = d; e(). There is only DynamicInvoke method but no direct invocation.
What is that Delegate (with cap letter) good for anyway if everybody suggests somehow to stay away from it?
I wish you guys not to link me to some other already existing answer. I would appreciate if somebody could take 5 min time to discuss with me here about this if possible. Thanks
Each delegate has a strong signature: both return types and argument types are required to be specified. The base Delegate class is here to materialize any delegate, but not with purpose to be called directly.
It's the same as the Enum class. It's a base class to help dealing with enums.
I think it might be because when you declare a delegate Del the compiler creates a class Del that sub-classes Delegate which is an abstract class. So
Del d = DoSomething;
is actually a shortcut for
Del d = new Del(DoSomething);
you can't do the same with Delegate because it is an abstract class, so
Delegate e = new Delegate(d);
is illegal.
Note that, per your code the snippet
Del d = DoSomething;
d();
will fail to compile, with a invalid signature error, as the call does not match the signature (you will need something like d("Hello world") to make it work. Also using a return value of d will yield a compile time error (e.g. var result = d("");), because the compiler knows that Del delegates have a void return.
When using the same thing via a Delegate instance, all that can be known about the value is that is invokable, and the return type and the parameters (in number or in type) are unknown. The system enables you to invoke the delegate indirectly, via DynamicInvoke, dynamic here meaning that any resolution and any errors will not be known until execution time.
In C#, delegate behave more like a list of function pointers, so that we can call a list of methods with the same method signature via a delegate.
After the initial assignment, we can add or subtract methods from a delegate using += (additive assignment) and -= (subtractive assignment).
Say, I have two methods.
public static void Method1(string message){
// ...
}
public static void Method2(string message){
// ...
}
Now, I declare a delegate type,
public delegate void Del(string message);
If I want to execute Method1, Method2 and again Method1 in order, I can create a delegate like this.
Del delList = Method1;
delList += Method2;
delList += Method1;
Then, later in the program, If I remove Method1 from it, which occurrence of Method1 is removed? Is there any rule governing that?
I believe that the better and more readable way is to create a new delegate and add methods you want excute in order, instead of changing an existing one. I'm just curious about how -= operator is implemented for C# delegates.
If the matching performed by -= doesn't remove the one you wanted, you can call GetInvocationList(), manipulate it how you want, and make a new delegate.
However, that's only possible when you have access to the delegate. For fields, which only have operator+= and operator-= (subscribe and unsubscribe) behaviors, you'd best avoid duplicates if you care about the order of the calls.
Actually, it would probably be best to avoid duplicates altogether.
To answer the question about the specific behavior (though I still maintain that relying on it is far too confusing), operator-= uses Delegate.Remove, which is documented as:
Removes the last occurrence of the invocation list of a delegate from the invocation list of another delegate.
(The documentation for the Delegate class itself says "Managed languages use the Combine and Remove methods to implement delegate operations. Examples include the AddHandler and RemoveHandler statements in Visual Basic and the += and -= operators on delegate types in C#.")
I'm a beginner with C# and can't find any answer for this :
Im trying to delegate Actions with some interface parameter , but push through functions with objects that extend this interface (or class)
// some class extending interface
public class IntEvent : IFace{}
public class MoveEvent : IFace{}
// function i like to use to verify Action
static void setAction(Action<IFace> evt) {
// evt ...
}
// function to delegate as Action
static void evtCheck(IntEvent evt) {
// some func
}
static void evtMove(MoveEvent evt) {
// some func
}
// class {
// call method inside class and delegate this function :
setAction(evtCheck);
setAction(evtMove);
I'm receiving an error that "evtCheck(IntEvent) cannot be converted to Action<IFace>" , even if IntEvent extends the IFace interface .
How should I solve this ? Maybe I have to use Func or Delegate ?
You can't do what you're trying to do - you're expecting covariance, but Action<T> is contravariant on its only type-parameter.
You can't do a method-group conversion from evtCheck to Action<IFace> because evtCheck needs a more specific type (IntEvent) as its argument than the more general IFace type that an Action<IFace> instance expects. If such a conversion were allowed, what would you expect to happen if the delegate were executed with an argument that implements IFace but is not an IntEvent?
I think you should have another look at your design because it looks like there's a flaw there, but if you want, you can create a lambda to force a cast of the argument to the desired type and accept the possibility of an InvalidCastException:
setAction(iFace => evtCheck((IntEvent)iface));
More likely, you might want to make evtCheck accept a more general type or setAction to accept a more specific delegate.
Action<T> is contravariant in T, so methods taking an Action<T> will also accept Action<U> where T is derived from U.
Your IntEvent class, however, implements the IFace interface, which means that if the compiler accepted your setAction() call, it would be possible to call evtCheck() with an argument that is another IFace-derivative and not IntEvent, a runtime error and a clear violation of type safety.
Obligatory Eric Lippert reference: Covariance and contravariance
EDIT: from your description it seems to me that what you really want is differentiation based on the method parameter's type, so e.g. you want a separate action for IntEvent, another one for (a hypothetical) DoubleEvent etc. If this is the case, you are actually after double virtual dispatch, which is not directly supported in C#, but you can simulate it using the Visitor design pattern.