Nigel DH
Nigel DH

Reputation: 207

Error when binding generic method delegate - Signature or Security Transparency

I'm using reflection to do some database queries and in creating delegates to speed up the use of reflection a bit I have stumbled upon the following error:

Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

I have two methods which create these delegates, both with similar structures, but one works and the other doesn't. The only major difference between them is that the one which doesn't work has more parameters and returns a List with a generic type where as the working one only takes a single parameter and returns a single value of a declared type rather than a generic T.

Here's some example code:

The Method

public List<T> GetConnections<T>(IElement element, bool getChildren, bool getParents) where T : IConnectionTable, new()
{
    // do some database stuff and return a List<T> where the constraints on
    // T follow the method description above.
}

Creating the Delegate

Simplified for clarity

private Func<IElement, bool, bool, List<IConnectionTable>> GetConnectionsDelegate(string connectionType)
{
    // Get the type element from the passed string.
    Type elementType = Type.GetType(connectionType, true);

    // Create the generic method using that type.
    MethodInfo method = typeof(MyClass).GetMethod("GetConnections", new Type[]{ typeof(IElement), typeof(bool), typeof(bool) });
    MethodInfo generic = method.MakeGenericMethod(elementType);

    // Create a delegate of the method to speed up subsequent queries.
    var converted = (Func<IElement, bool, bool, List<IConnectionTable>>)Delegate.CreateDelegate(typeof(Func<IElement, bool, bool, List<IConnectionTable>>), this, generic);

    // the above line is where it dies
}

The actual code saves the delegate into a private static dictionary so I only have to use reflection once.

If I print out the contents of method and generic it all seems to be correctly converted.

Result StandardOutput:  

System.Collections.Generic.List`1[T] GetConnections[T](MyProject.Database.IElement, Boolean, Boolean)
System.Collections.Generic.List`1[MyTestProject.TestConnection] GetConnections[TestConnection](MyProject.Database.IElement, Boolean, Boolean)

My assumption here is that the problems lie in the difference between the generic List vs IConnectionTable List return types but making the method return a non-generic list results in a lot of cast errors in the generic method and is kinda wrong to do anyway. Plus, that code runs fine when tested.

It shouldn't be the difference between the private and public methods as my other delegate creation methods are the same and work fine (I've also tried changing GetConnectionsDelegate to public and it makes no difference.)

Any help would be much appreciated.

ndh

Upvotes: 1

Views: 2713

Answers (1)

David L
David L

Reputation: 33823

The reason you are running into difficulties actually has nothing to do with your generic invocation, but rather your return type. Generic classes do not support covariance, which is essentially what you are trying to accomplish by returning List<IConnectionTable> when using a concrete type that implements IConnectionTable.

A workaround for this is to use a covariant interface collection, such as IEnumerable<T>. In addition, you need to add an actual instance via the second parameter, since this is probably pointing at the incorrect context.

var converted = (Func<IElement, bool, bool, IEnumerable<IConnectionTable>>)
    Delegate.CreateDelegate(typeo‌​f(Func<IElement, bool, bool, IEnumerable<IConnectionTable>>), new MyClass(), generic);

In addition, you might want to consider compiled expressions instead of delegates since they play nicely with your situation and give you more readability and potentially more flexibility. You will want to profile each methodology and determine which performs better.

The following is a simple cached compiled expression example that correctly outputs "we got called" from the compiled expression.

void Main()
{
    var key = typeof(ConnectionTypeOne).FullName;
    Func<IElement, bool, bool, IEnumerable<IConnectionTable>> expr = 
        _cache.ContainsKey(key) ? _cache[key]
        : CreateConnectionExpression<ConnectionTypeOne>(key);

    expr(new Element(), true, true);
}

private static IDictionary<string, Func<IElement, bool, bool, IEnumerable<IConnectionTable>>> _cache = 
    new Dictionary<string, Func<IElement, bool, bool, IEnumerable<IConnectionTable>>>();

private Func<IElement, bool, bool, IEnumerable<IConnectionTable>> CreateConnectionExpression<T>(string connectionType)
    where T : IConnectionTable
{
    // Get the type element from the passed string.
    Type elementType = Type.GetType(connectionType, true);

    // Create the generic method using that type.
    MethodInfo method = typeof(MyClass).GetMethod("GetConnections", new Type[] { typeof(IElement), typeof(bool), typeof(bool) });
    MethodInfo generic = method.MakeGenericMethod(elementType);

    var instance = Expression.Constant(new MyClass());
    var c1 = Expression.Parameter(typeof(IElement));
    var c2 = Expression.Parameter(typeof(bool));
    var c3 = Expression.Parameter(typeof(bool));

    var expr = Expression.Call(instance, generic, c1, c2, c3);
    Func<IElement, bool, bool, IEnumerable<IConnectionTable>> compiledExpr =
        (Func<IElement, bool, bool, IEnumerable<IConnectionTable>>)
            Expression.Lambda(expr, c1, c2, c3).Compile();

    _cache[connectionType] = compiledExpr;

    return compiledExpr;
}

public class MyClass 
{
    public List<T> GetConnections<T>(IElement element, bool getChildren, bool getParents) 
        where T : IConnectionTable, new()
    {
        Console.WriteLine("we got called");
        return new List<T>();
    }
}

public interface IElement { }
public interface IConnectionTable { }

public class Element : IElement { }
public class ConnectionTypeOne : IConnectionTable { }

Upvotes: 3

Related Questions