2016-09-07 27 views
1

我使用反射做一些數據庫查詢和創建委託加快使用反射一點我已經在下面的錯誤絆倒:錯誤時結合泛型方法的委託 - 簽名或安全透明

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

我有兩種創建這些委託的方法,它們都具有相似的結構,但一個可以工作,另一個不可以。它們之間唯一的主要區別是不工作的參數有更多的參數,並且返回帶有泛型類型的List,因爲工作參數只接受單個參數並返回聲明類型的單個值而不是泛型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) 

我在這裏的假設是,問題在於VS IConnectionTable列表返回值類型的泛型列表之間的差別,使得該方法在許多鑄造錯誤的泛型方法返回一個非泛型列表的結果,是反正有點不對。另外,測試時代碼運行良好。

它不應該是私人和公共方法的差異爲我的其他委託創建方法是一樣的,做工精細(我也嘗試改變GetConnectionsDelegate公衆並沒有什麼區別。)

任何幫助將不勝感激。

NDH

+0

您是否正在創建代表以加快一些反射開銷,而不是真正的db查詢? –

+1

是的,這是加速反思。我想這與代碼本身做什麼並不重要。 –

+0

謝謝。那麼,有時把它放到透視圖中可能會有用...... –

回答

2

您正在運行陷入困境的原因其實無關,與一般的調用,而是你的返回類型。泛型類不支持協方差,這實質上就是您在使用實現IConnectionTable的具體類型時返回List<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更快,但您需要配置文件。 Delegate.CreateDelegate(typeof(Func >), \t \t \t new MyClass(()){var Func = ),通用);' –