通过表达式选择方法时,没有重载匹配委托 'Action'
No Overload matches delegate 'Action' when selecting method via an expression
我正在尝试创建一个流畅的 API 允许 selecting class 方法。尝试 select 带有参数的方法时,出现错误:
No overload for 'MethodB' matches delegate 'Action'
.
C# 似乎没有自动确定正确的泛型方法。如果我在调用签名中指定泛型类型,它会起作用,但如果省略,则不会。通常,如果我将此技术用于 select 属性或字段,泛型类型将自动确定,但我怀疑 C#(或 Visual Studio)无法自动确定类型,因为它是包裹在 Action<>
.
中
有没有一种方法可以确保调用此方法不需要声明显式泛型?如果没有,我很想知道为什么。
public class Program
{
public static void Main( string[] args )
{
var builder = new OperationBuilder<SomeClass>();
builder.AddMethod( x => x.MethodA );
builder.AddMethod( x => x.MethodB ); // Gives error
builder.AddMethod<double>( x => x.MethodB ); // Works correctly
}
public class OperationBuilder<T>
where T : class
{
private List<MethodInfo> methods;
public OperationBuilder()
{
methods = new List<MethodInfo>();
}
public OperationBuilder<T> AddMethod( MethodInfo method )
{
methods.Add( method );
return this;
}
public OperationBuilder<T> AddMethod( Expression<Func<T, Action>> expression )
=> AddMethod( expression.GetMethodInfo() );
public OperationBuilder<T> AddMethod<T1>( Expression<Func<T, Action<T1>>> expression )
=> AddMethod( expression.GetMethodInfo() );
}
public class SomeClass
{
public void MethodA()
{
}
public void MethodB( double value )
{
}
}
}
public static class ExpressionExtensions
{
public static MethodInfo GetMethodInfo<TClass>(
this Expression<Func<TClass, Action>> expression )
=> GetMethodInfoInternal( expression );
public static MethodInfo GetMethodInfo<TClass, T1>(
this Expression<Func<TClass, Action<T1>>> expression )
=> GetMethodInfoInternal( expression );
private static MethodInfo GetMethodInfoInternal( LambdaExpression expression )
{
if( !( expression.Body is UnaryExpression unary ) )
throw new ArgumentException(
"Expression is not unary.",
nameof( expression ) );
if( !( unary.Operand is MethodCallExpression methodCall ) )
throw new ArgumentException(
"Expression is not a method call.",
nameof( expression ) );
if( !( methodCall.Object is ConstantExpression constant )
|| !( constant.Value is MethodInfo methodInfo ) )
throw new ArgumentException(
"Expression does not contain a valid method reference.",
nameof( expression ) );
return methodInfo;
}
}
通过指定 x.MethodB
,您实际上并没有直接提供委托,而是 so-called method group. Method groups can be implicitly converted to a given compatible delegate type.
因此,编译器在尝试推断您的 AddMethod<T1>
方法的泛型类型参数 T1
时会面临以下情况。为了能够推断 T1
,编译器需要知道手头的具体 Action<T1>
委托类型。但是编译器给出的只是一个方法组,而不是 Action<double>
委托。
为了能够(隐式)将方法组转换为委托,编译器需要知道方法组应转换为的兼容委托类型。但是编译器只有在可以推断 T1
时才能知道这个委托类型,这又需要它知道具体的 Action<T1>
类型。第二十二条军规
一个类似但更简单的问题,说明了相同的潜在问题:
static void SomeMethod<T1>(Action<T1> action) { }
var sc = new SomeClass();
SomeMethod(sc.MethodB); // compile error, type argument cannot be inferred
在这里,编译器也无法推断出 SomeMethod 的 类型参数 T1
,因为它被赋予了一个方法组,而不是一个 Action<T1>
委托.而且它不能成功地将方法组转换为委托,因为要确定转换的实际具体 Action<T1>
类型,它需要有一个具体的 Action<T1>
类型来推断 T1
.
下面是一个小的(愚蠢且不切实际的)示例,表明问题不在于 T1
本身的推断,而是缺少给定的具体 Action<T1>
委托类型 T1
可以推断。在这里,只在 lambda 表达式中给出了一个 Action<double>
委托,这反过来将允许编译器推断 T1
:
var sc = new SomeClass();
Action<double> d = sc.MethodB;
builder.AddMethod(x => d); // Gives no error anymore
或者,不那么傻:
builder.AddMethod(x => (Action<double>) x.MethodB); // Gives no error anymore
我正在尝试创建一个流畅的 API 允许 selecting class 方法。尝试 select 带有参数的方法时,出现错误:
No overload for 'MethodB' matches delegate 'Action'
.
C# 似乎没有自动确定正确的泛型方法。如果我在调用签名中指定泛型类型,它会起作用,但如果省略,则不会。通常,如果我将此技术用于 select 属性或字段,泛型类型将自动确定,但我怀疑 C#(或 Visual Studio)无法自动确定类型,因为它是包裹在 Action<>
.
有没有一种方法可以确保调用此方法不需要声明显式泛型?如果没有,我很想知道为什么。
public class Program
{
public static void Main( string[] args )
{
var builder = new OperationBuilder<SomeClass>();
builder.AddMethod( x => x.MethodA );
builder.AddMethod( x => x.MethodB ); // Gives error
builder.AddMethod<double>( x => x.MethodB ); // Works correctly
}
public class OperationBuilder<T>
where T : class
{
private List<MethodInfo> methods;
public OperationBuilder()
{
methods = new List<MethodInfo>();
}
public OperationBuilder<T> AddMethod( MethodInfo method )
{
methods.Add( method );
return this;
}
public OperationBuilder<T> AddMethod( Expression<Func<T, Action>> expression )
=> AddMethod( expression.GetMethodInfo() );
public OperationBuilder<T> AddMethod<T1>( Expression<Func<T, Action<T1>>> expression )
=> AddMethod( expression.GetMethodInfo() );
}
public class SomeClass
{
public void MethodA()
{
}
public void MethodB( double value )
{
}
}
}
public static class ExpressionExtensions
{
public static MethodInfo GetMethodInfo<TClass>(
this Expression<Func<TClass, Action>> expression )
=> GetMethodInfoInternal( expression );
public static MethodInfo GetMethodInfo<TClass, T1>(
this Expression<Func<TClass, Action<T1>>> expression )
=> GetMethodInfoInternal( expression );
private static MethodInfo GetMethodInfoInternal( LambdaExpression expression )
{
if( !( expression.Body is UnaryExpression unary ) )
throw new ArgumentException(
"Expression is not unary.",
nameof( expression ) );
if( !( unary.Operand is MethodCallExpression methodCall ) )
throw new ArgumentException(
"Expression is not a method call.",
nameof( expression ) );
if( !( methodCall.Object is ConstantExpression constant )
|| !( constant.Value is MethodInfo methodInfo ) )
throw new ArgumentException(
"Expression does not contain a valid method reference.",
nameof( expression ) );
return methodInfo;
}
}
通过指定 x.MethodB
,您实际上并没有直接提供委托,而是 so-called method group. Method groups can be implicitly converted to a given compatible delegate type.
因此,编译器在尝试推断您的 AddMethod<T1>
方法的泛型类型参数 T1
时会面临以下情况。为了能够推断 T1
,编译器需要知道手头的具体 Action<T1>
委托类型。但是编译器给出的只是一个方法组,而不是 Action<double>
委托。
为了能够(隐式)将方法组转换为委托,编译器需要知道方法组应转换为的兼容委托类型。但是编译器只有在可以推断 T1
时才能知道这个委托类型,这又需要它知道具体的 Action<T1>
类型。第二十二条军规
一个类似但更简单的问题,说明了相同的潜在问题:
static void SomeMethod<T1>(Action<T1> action) { }
var sc = new SomeClass();
SomeMethod(sc.MethodB); // compile error, type argument cannot be inferred
在这里,编译器也无法推断出 SomeMethod 的 类型参数 T1
,因为它被赋予了一个方法组,而不是一个 Action<T1>
委托.而且它不能成功地将方法组转换为委托,因为要确定转换的实际具体 Action<T1>
类型,它需要有一个具体的 Action<T1>
类型来推断 T1
.
下面是一个小的(愚蠢且不切实际的)示例,表明问题不在于 T1
本身的推断,而是缺少给定的具体 Action<T1>
委托类型 T1
可以推断。在这里,只在 lambda 表达式中给出了一个 Action<double>
委托,这反过来将允许编译器推断 T1
:
var sc = new SomeClass();
Action<double> d = sc.MethodB;
builder.AddMethod(x => d); // Gives no error anymore
或者,不那么傻:
builder.AddMethod(x => (Action<double>) x.MethodB); // Gives no error anymore