将方法分配给委托,其中委托具有比方法更多的参数

本文关键字:方法 参数 分配 | 更新日期: 2023-09-27 18:17:27

我有一个委托,它有很多参数,像这样:

public delegate void MyDelegate(float thereAre, int lotsOf, string parametersIn, int thisDelegate);
public MyDelegate theDelegateInstance;

这很烦人,因为Visual Studio 2010没有任何类型的自动完成来帮助方法匹配委托签名。我基本上希望能够编写一个方法,它只接受委托的一些(或没有)参数,而忽略其他的,因为它无论如何都不使用它们。

theDelegateInstance += delegate()
{
    Debug.Log("theDelegateInstance was called");
};

theDelegateInstance += delegate(float thereAre, int lotsOf)
{
    if(thereAre > lotsOf) Debug.Log("thereAre is way too high");
};

我发现我可以让一个方法接受一个委托,返回一个MyDelegate,像这样调用它:

public delegate void VoidMethod();
public static MyDelegate ConvertToMyDelegate(VoidMethod method)
{
    return delegate(float thereAre, int lotsOf, string parametersIn, int thisDelegate)
    {
        method();
    };
}

但是这需要我为每个不同的转换声明一个静态方法。

我刚刚发现我可以做我的第一个例子,没有任何参数,以达到预期的结果:

theDelegateInstance += delegate//Notice that there are no brackets here.
{
    Debug.Log("theDelegateInstance was called");
};

但这只适用于不带参数的内联方法。如果我想像第二个例子那样使用其中一个参数,我就需要拥有所有的参数。

将方法分配给委托,其中委托具有比方法更多的参数

你总是可以用lambdas来做。

你可以通过两种方式来实现——使用你想调用的两个示例函数:

第一种方法-创建方法,并直接调用它:

void FirstFunction(float thereAre, int lotsOf)
{
    if(thereAre > lotsOf) 
        Debug.Log("thereAre is way too high");
}

并以这种方式调用它:

theDelegateInstance += (t, l, p, td) => FirstFunction(t, l);

第二种方法-直接调用而不创建函数:

theDelegateInstance += 
    (t, l, p, td) => Debug.Log("theDelegateInstance was called");

这是可能的。你只需要在委托中使用一个可选参数。

看Jon Skeet的[答案]

可选参数是在调用端使用的,而不是像单方法接口实现那样有效地使用。例如,这应该编译为:

delegate void SimpleDelegate(bool x = true);
static void Main()
{
    SimpleDelegate x = Foo;
    x(); // Will print "True"
 }
 static void Foo(bool y)
 {
     Console.WriteLine(y);
 }

(委托的可选参数不能正常工作)。

基本上,你所要求的是一个方法,返回一个lambda像这样:

public static MyDelegate ConvertToMyDelegate(VoidMethod method)
{
    return (thereAre, lotsOf, parametersIn, thisDelegate) => method();
}

幸运的是,. net包含了一种以编程方式创建lambdas的方法。这意味着你可以只创建一个通用的Convert方法,它将处理两种委托类型中的任意数量的参数:

public static TTarget ConvertDelegate<TSource, TTarget>(TSource sourceDelegate)
{
    if (!typeof(Delegate).IsAssignableFrom(typeof(TSource)))
        throw new InvalidOperationException("TSource must be a delegate.");
    if (!typeof(Delegate).IsAssignableFrom(typeof(TTarget)))
        throw new InvalidOperationException("TTarget must be a delegate.");
    if (sourceDelegate == null)
        throw new ArgumentNullException("sourceDelegate");
    var parameterExpressions = typeof(TTarget)
        .GetMethod("Invoke")
        .GetParameters()
        .Select(p => Expression.Parameter(p.ParameterType))
        .ToArray();
    var sourceParametersCount = typeof(TSource)
        .GetMethod("Invoke")
        .GetParameters()
        .Length;
    var expression = Expression.Lambda<TTarget>(
        Expression.Invoke(
            Expression.Constant(sourceDelegate),
            parameterExpressions.Take(sourceParametersCount)),
        parameterExpressions);
    return expression.Compile();
}