绑定泛型方法委托时出错 - 签名或安全透明度
Error when binding generic method delegate - Signature or Security Transparency
我正在使用反射进行一些数据库查询,并在创建委托以稍微加快反射的使用时偶然发现以下错误:
Cannot bind to the target method because its signature or security
transparency is not compatible with that of the delegate type.
我有两种创建这些委托的方法,两种方法都具有相似的结构,但一种有效,另一种无效。它们之间唯一的主要区别是,不起作用的那个有更多的参数,returns 是一个具有泛型类型的列表,而工作的那个只需要一个参数,returns 是一个单一的值声明的类型而不是泛型 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
}
实际代码将委托保存到私有静态字典中,因此我只需使用反射一次。
如果我打印出 method 和 generic 的内容,它似乎都已正确转换。
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)
我的假设是,问题在于通用列表与 IConnectionTable 列表 return 类型之间的差异,但是使方法 return 成为非通用列表会导致大量转换错误在通用方法中,无论如何都是错误的。另外,该代码在测试时运行良好。
这不应该是私有方法和 public 方法之间的区别,因为我的其他委托创建方法是相同的并且工作正常(我也试过更改 GetConnectionsDelegate 到 public,这没有区别。)
如有任何帮助,我们将不胜感激。
ndh
您 运行 陷入困境的原因实际上与您的通用调用无关,而是您的 return 类型。泛型 类 不支持协变,这实际上是您在使用实现 IConnectionTable
的具体类型时通过 returning List<IConnectionTable>
试图实现的。
解决此问题的方法是使用协变接口集合,例如 IEnumerable<T>
。此外,您需要通过第二个参数添加一个实际实例,因为 this
可能指向不正确的上下文。
var converted = (Func<IElement, bool, bool, IEnumerable<IConnectionTable>>)
Delegate.CreateDelegate(typeof(Func<IElement, bool, bool, IEnumerable<IConnectionTable>>), new MyClass(), generic);
此外,您可能需要考虑编译表达式而不是委托,因为它们可以很好地适应您的情况,并为您提供更多的可读性和潜在的灵活性。您需要分析每种方法并确定哪种方法效果更好。
下面是一个简单的缓存编译表达式示例,从编译表达式正确输出"we got called"。
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 { }
我正在使用反射进行一些数据库查询,并在创建委托以稍微加快反射的使用时偶然发现以下错误:
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
我有两种创建这些委托的方法,两种方法都具有相似的结构,但一种有效,另一种无效。它们之间唯一的主要区别是,不起作用的那个有更多的参数,returns 是一个具有泛型类型的列表,而工作的那个只需要一个参数,returns 是一个单一的值声明的类型而不是泛型 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
}
实际代码将委托保存到私有静态字典中,因此我只需使用反射一次。
如果我打印出 method 和 generic 的内容,它似乎都已正确转换。
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)
我的假设是,问题在于通用列表与 IConnectionTable 列表 return 类型之间的差异,但是使方法 return 成为非通用列表会导致大量转换错误在通用方法中,无论如何都是错误的。另外,该代码在测试时运行良好。
这不应该是私有方法和 public 方法之间的区别,因为我的其他委托创建方法是相同的并且工作正常(我也试过更改 GetConnectionsDelegate 到 public,这没有区别。)
如有任何帮助,我们将不胜感激。
ndh
您 运行 陷入困境的原因实际上与您的通用调用无关,而是您的 return 类型。泛型 类 不支持协变,这实际上是您在使用实现 IConnectionTable
的具体类型时通过 returning List<IConnectionTable>
试图实现的。
解决此问题的方法是使用协变接口集合,例如 IEnumerable<T>
。此外,您需要通过第二个参数添加一个实际实例,因为 this
可能指向不正确的上下文。
var converted = (Func<IElement, bool, bool, IEnumerable<IConnectionTable>>)
Delegate.CreateDelegate(typeof(Func<IElement, bool, bool, IEnumerable<IConnectionTable>>), new MyClass(), generic);
此外,您可能需要考虑编译表达式而不是委托,因为它们可以很好地适应您的情况,并为您提供更多的可读性和潜在的灵活性。您需要分析每种方法并确定哪种方法效果更好。
下面是一个简单的缓存编译表达式示例,从编译表达式正确输出"we got called"。
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 { }