2016-09-07 5 views
1

Я использую отражение, чтобы сделать некоторые запросы к базе данных и в создании делегатов ускорить использование отражения немного я наткнулся на следующее сообщение об ошибке:Ошибка при связывании общий метод делегата - Подпись или безопасности Прозрачность

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

У меня есть два метода, которые создают этих делегатов, как с аналогичными структурами, но один работает, а другой нет. Единственное существенное различие между ними заключается в том, что тот, который не работает, имеет больше параметров и возвращает список с общим типом, где, поскольку рабочий принимает только один параметр и возвращает одно значение объявленного типа, а не общее T .

Вот пример кода:

Метод

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. 
} 

Создание делегата

упрощенный для ясности

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 
} 

Реальный код сохраняет делегат в частный статический словарь, так что я только должен использовать отражение один раз.

Если я распечатать содержимое метода и родового все это, кажется, правильно преобразованы.

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) 

Мое предположение, что проблемы лежат в различии между родовым List против типов возврата IConnectionTable списка, но сделать метод возвращающим необщего списка результаты в большом количестве ошибок отлитых в родовой методе и что-то нехорошо делать. Кроме того, этот код работает нормально при тестировании.

Она не должна быть разница между частными и государственными методами, как другие мои методы создания делегата одинаковы и работают отлично (я также попытался изменить GetConnectionsDelegate публике, и это не имеет никакого значения.)

Любая помощь будет очень признательна.

NDH

+0

Вы создаете делегатов, чтобы ускорить некоторые накладные расходы, а не запросы db? –

+1

Да, это ускорить отражение. Думаю, это не имеет значения, что я делаю с самим кодом. –

+0

Спасибо. Ну, иногда его использование в перспективе может быть полезно ... –

ответ

2

Причина вы работаете с трудностями на самом деле не имеет ничего общего с родовым вызова, а ваш тип возвращаемого значения. Общие классы не поддерживают ковариацию, что по существу является тем, что вы пытаетесь выполнить, возвращая List<IConnectionTable> при использовании конкретного типа, который реализует IConnectionTable.

Обходным путем для этого является использование коллекции ковариантных интерфейсов, например IEnumerable<T>. Кроме того, вам нужно добавить фактический экземпляр через второй параметр, так как this, вероятно, указывает на неправильный контекст.

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

Кроме того, вы можете рассмотреть скомпилированные выражения вместо делегатов, так как они играют хорошо с вашей ситуацией и дать вам больше читаемости и потенциально большую гибкость. Вы захотите просмотреть каждую методологию и определить, какая из них лучше.

Ниже приведен простой пример использования кэшированного скомпилированного выражения, который правильно выводит «мы получили вызов» из скомпилированного выражения.

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 { } 
+0

Переключение на IEnumerable получило трюк, огромное спасибо. Вопрос. Есть ли определенный выигрыш в использовании скомпилированных выражений над делегатами? Я все еще новичок в C#, поэтому разница, отличная от того, как они технически собраны вместе, мне не понятна. –

+1

@NigelDH Скомпилированные выражения, по существу, сводятся к делегату func, поэтому то, что вы получаете, может быть только читабельностью и некоторой модулярностью, если вам нужно создать несколько разновидностей делегатов. В некоторых случаях это может быть быстрее, чем CreateDelegate, но вам нужно профиль. Соответствующим вызовом CreateDelegate будет 'var convert = (Func >) Delegate.CreateDelegate (typeof (Func >), \t \t новый MyClass (), общий); –

 Смежные вопросы

  • Нет связанных вопросов^_^