Home > Enterprise >  No overload for 'DoSomethingInt' matches delegate 'DoSomethingDelegate<T>'
No overload for 'DoSomethingInt' matches delegate 'DoSomethingDelegate<T>'

Time:04-18

class DelegateTest<T> where T: // ....
{
    public DelegateTest(DoSomethingDelegate<T> action = null)
    {
        if (action != null)
            _doSomething = action;
        else if (typeof(T) == typeof(int))
            //_doSomething = DoSomethingInt; // fails
            //_doSomething = (DoSomethingDelegate<T>)DoSomethingInt; // fails
            //_doSomething = (DoSomethingDelegate<T>)((Delegate)DoSomethingInt); // fails
            _doSomething = (val) => DoSomethingInt((int)((object)val)); // too ugly
        else //  ...
    }

    public void DoSomethingVeryComplex(T val)
    {
        // ....
        _doSomething(val);
        // ....
    }

    DoSomethingDelegate<T> _doSomething;

    static void DoSomethingInt(int val)
    {
    }
}

delegate void DoSomethingDelegate<T>(T val);

So, the delegate and the method are compatible by the signature, but this assignment doesn't work for some obscure reasons and I'm totally out of ideas how to make it work. Any better ideas other than to create a shim function?
This question is not about how this code can be rewritten in other ways. It's only about how to make this assignment work, and if that's impossible - why.

CodePudding user response:

You can cast and assign the delegate directly if you use Action<T> instead of your custom delegate.

class DelegateTest<T>
{
    public DelegateTest()
    {
        if (typeof(T) == typeof(int))
        {
            _doSomething = (Action<T>)(object)DoSomethingInt;
        }
    }

    Action<T> _doSomething;

    public void InvokeDoSomething(T x) => _doSomething(x);

    static void DoSomethingInt(int val)
    {
        Console.WriteLine("Hello world! The argument was {0}", val);
    }
}

Quick test:

public class Program
{
    public static async Task Main()
    {
        var o = new DelegateTest<int>();
        o.InvokeDoSomething(1);
    }
}

Output:

Hello world! The argument was 1

Fiddle

CodePudding user response:

What you are doing doesn't make sense. You don't use If statements to look for specific types inside a generic type. The whole point of generics is that they work for multiple types with common functionality.

This is the sort of thing that would make sense:

internal class Program
{
    static void Main(string[] args)
    {
        var intDT = new DelegateTest<int>();
        var stringDT = new DelegateTest<string>();

        intDT.Action = IntAction;
        stringDT.Action = StringAction;

        intDT.Action(10);
        stringDT.Action("Hello");
    }

    static void IntAction(int number)
    {
        Console.WriteLine(number * 2);
    }

    static void StringAction(string text)
    {
        Console.WriteLine($"{text}, {text}");
    }
}

class DelegateTest<T>
{
    public Action<T> Action;
}

Note that there's really no point declaring your own delegate when you can use the Action or Func delegates.

The reason that your code doesn't work is that you are trying to assign a delegate with a parameter of type int to a delegate variable with a parameter of type T. The fact that it's inside an if statement is irrelevant because that doesn't get executed until run time. That code has to be valid at compile time, which means that it has to work for any type that T might be. In my code above, I don't assign to the delegate variable until the type of T has already been fixed, so the compiler knows that the type of the delegate I'm assigning matches the type of the variable I'm assigning it to.

CodePudding user response:

If what you're trying to achieve is call one of several internal methods based on the generic type of the class then a generic delegate is not the way to do it. It would be bizarre to want to do that in the first place but this would be a better way to achieve it:

class DelegateTest<T>
{
    public void DoSomething<T>(T val)
    {
        switch (val)
        {
            case int n:
                DoSomethingInt(n);
                break;
            case string s:
                DoSomethingString(s);
                break;
        }
    }

    public void DoSomethingInt(int val)
    {
        Console.WriteLine(val * 2);
    }

    public void DoSomethingString(string val)
    {
        Console.WriteLine($"{val}, {val}");
    }
}

You can then do this:

var intDT = new DelegateTest<int>();
var stringDT = new DelegateTest<string>();

intDT.DoSomething(10);
stringDT.DoSomething("Hello");

which is basically equivalent to what you'd do with the delegate.

  • Related