Search code examples
c#delegatesanonymous-delegates

Can't assign anonymous delegate to signed delegate


I am looking for a way to assign different delegates in a list to MethodInfo's without having information about the return types beforehand. Below is the code code I am using. The comments give additional information on what is happening. It is a lengthy bit of code but I have reduced it as much as I can.

Main Snippet

private const string methodName = "Execute";

    public static void Main()
    {
        ExampleClass1 e1 = new ExampleClass1();
        ExampleClass2 e2 = new ExampleClass2();
        ExampleClass3 e3 = new ExampleClass3();

        /* Code below Simulates:  "e3.GetString = e2.Execute;" */
        var method = e2.GetType().GetMethod(methodName);

        for (int i = 0; i < e3.DelegateList.Count; i++)
        {
            // First check the type of e2 return
            Type methodType = method.ReturnType;

            // Check that its the same return type as delegate
            if (methodType != e3.DelegateList[i].ReturnType)
                continue;

            // Assign delegate to method
            var returnType = e3.DelegateList[i].DelegateType;
            e3.DelegateList[i].Delegate = Delegate.CreateDelegate(returnType, e2, method);

            /* Code below only for debugging */
            Console.WriteLine("The delegate in the list: " + e3.DelegateList[i].Delegate);// Returns Type of StringHandler
            Console.WriteLine("The delegate in the object: " + e3.GetString);// Returns null
            e3.GetString = e3.DelegateList[i].Delegate;// This throws Error Cannot convert Delegate to StringHandler
        }

        /* Code above Simulates:  "e3.GetString = e2.Execute;" */

        e2.GetNumber = e1.Execute;

        e3.Execute();// Throws Null References Exception on

        // Read the key
        Console.ReadKey();
    }

Supporting Classes/Code

If you need more information about supporting classes, please see the code below. In addition, this is a self-contained program and should be runnable as is.

 public class ExampleClass3
{
    public delegate string StringHandler();

    public delegate int IntHandler();

    public StringHandler GetString { get; set; }
    public IntHandler GetInt { get; set; }

    public List<DelegateInfo<Type, Type, Delegate>> DelegateList { get; set; }

    public ExampleClass3()
    {
        DelegateList = new List<DelegateInfo<Type, Type, Delegate>>();
        DelegateList.Add(new DelegateInfo<Type, Type, Delegate>(typeof(StringHandler), typeof(string), GetString));
        DelegateList.Add(new DelegateInfo<Type, Type, Delegate>(typeof(IntHandler), typeof(int), GetInt));
    }

    public object Execute()
    {
        Console.WriteLine(GetString());

        return null;
    }
}

public class ExampleClass2
{
    public delegate int NumberHandler();

    public NumberHandler GetNumber { get; set; }

    public string Execute() => $"Your Number Is {GetNumber()}";
}

public class ExampleClass1
{
    public int number = 5;

    public int Execute() => number;
}

public class DelegateInfo<T1, T2, T3>
{
    public DelegateInfo(T1 delegateType, T2 returnType, T3 @delegate)
    {
        DelegateType = delegateType;
        ReturnType = returnType;
        Delegate = @delegate;
    }

    public T1 DelegateType { get; set; }
    public T2 ReturnType { get; set; }
    public T3 Delegate { get; set; }
}

Solution

  • I simplified your code a bit to demonstrate how I would go about this. First off, don't create a special DelegateInfo class - stick with the standard .NET reflection library as much as possible. They did a really good job on it - but it does take a while to learn.

    Here is the code:

        private const string methodName = "Execute";
    
        public static void Main()
        {
            ExampleClass1 e1 = new ExampleClass1();
            ExampleClass2 e2 = new ExampleClass2();
            ExampleClass3 e3 = new ExampleClass3();
    
            /* Code below Simulates:  "e3.GetString = e2.Execute;" */
            var method = e2.GetType().GetMethod(methodName);            
            Type methodType = method.ReturnType;
    
            // Create a Func<T> that will invoke the target method            
            var funcType = typeof(Func<>).MakeGenericType(methodType);
            var del = Delegate.CreateDelegate(funcType, e2, method);
    
            var properties = e3.GetType().GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {              
                if (properties[i].PropertyType.IsAssignableFrom(funcType)) {
                    properties[i].SetValue(e3, del );
                }
            }
    
            /* Code above Simulates:  "e3.GetString = e2.Execute;" */
    
            e2.GetNumber = e1.Execute;
    
            e3.Execute();
    
            // Read the key
            Console.ReadKey();
        }
        public class ExampleClass3
        {         
            public Func<String> GetString { get; set; }
            public Func<int> GetInt { get; set; }
    
            public ExampleClass3()
            { }
    
            public object Execute()
            {
                Console.WriteLine(GetString());
                return null;
            }
        }
    
        public class ExampleClass2
        {
            public Func<int> GetNumber { get; set; }
    
            public string Execute() => $"Your Number Is {GetNumber()}";
        }
    
        public class ExampleClass1
        {
            public int number = 5;
    
            public int Execute() => number;
        }
    

    First off, note how I got rid of the custom delegate definitions in favor of Func. This will prove much easier to work with in a generic fashion. Note how ExampleClass3 is defined now:

        public class ExampleClass3
        {         
            public Func<String> GetString { get; set; }
            public Func<int> GetInt { get; set; }
    
            public ExampleClass3()
            { }
    
            public object Execute()
            {
                Console.WriteLine(GetString());
                return null;
            }
        }
    

    I can use the fact that all these functions are of type Func to develop a generic solution to assigning them a value. Based upon the return type of the target method, I can construct a Func delegate of the appropriate type (and link it to the specific e2 instance in question):

        var funcType = typeof(Func<>).MakeGenericType(methodType);
        var del = Delegate.CreateDelegate(funcType, e2, method);
    

    Now I can directly assign this delegate as the value of any properties with a matching delegate type:

            var properties = e3.GetType().GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {              
                if (properties[i].PropertyType.IsAssignableFrom(funcType)) {
                    properties[i].SetValue(e3, del );
                }
            } 
    

    Hope that helps :)