泛型类型的扩展方法,带有其他泛型类型参数

时间:2010-12-16 01:17:06

标签: generics extension-methods type-inference

我想为泛型类型编写扩展方法,并使用其他泛型类型参数。我已经有了工作代码,但不喜欢结果,因为用户需要重新输入现有类型的泛型类型参数。

我有一个具体的例子,但请记住,这是一个普遍的问题。我很欣赏有关手头特定问题的反馈,但我正在寻找一般解决方案。

对于类型Action<T>的类,添加一个带有与此类似的签名的扩展方法:

Func<T, TResult> ToFunc<TResult>();

这是我的工作代码:

public static class DelegateExtensions
{
  public static Func<T, TResult> ToFunc<T, TResult>(this Action<T> action)
  {
    return arg => { action(arg); return default(TResult); };
  }
}

但是用法很糟糕:

public void TakesAFunc(Func<int, float> someFunc) { /* ... */ }
// ...
Action<int> someIntAction = /* ... */;
TakesAFunc(someIntAction.ToFunc<int, float>());

在此示例中,int泛型参数是唯一可接受的值,因此会导致不必要的代码重复。

类型推断在这里不起作用。我认为这是因为您无法通过返回类型推断出通用参数。

此代码可以解决问题,但遗憾的是似乎不起作用:

public static class DelegateExtensions<T>
{
  public static Func<T, TResult> ToFunc<TResult>(this Action<T> action)
  {
    return arg => { action(arg); return default(TResult); };
  }
}

用法与您期望的完全一样:

public void TakesAFunc(Func<int, float> someFunc) { /* ... */ }
// ...
Action<int> someIntAction = /* ... */;
TakesAFunc(someIntAction.ToFunc<float>());

我注意到System.Linq.Queryable的工作方式与我的第一个代码块相同,但它通常不需要额外的类型参数,因此类型推断可以工作。

是否有任何已知的技巧可以解决这些重复的泛型类型参数?想到的一个想法是代码生成器或某种类型的宏,但我想不出我是如何干净地完成的。

2 个答案:

答案 0 :(得分:3)

这类事情的一个常见技巧是为类型推断传递所需类型的虚拟值,这在某些涉及匿名类型的场景中是必需的。

public static class DelegateExtensions
{
  public static Func<T, TResult> 
  ToFunc<T, TResult>(this Action<T> action, TResult ignored)
  {
    return arg => { action(arg); return default(TResult); };
  }
}

这是多么好的伎俩取决于个人意见,但每当我打电话给我时,每次我回到我称之为的地方时,这都会让我感到困惑。您还可以更改方法的语义,使第二个参数成为返回值,允许调用者决定是否需要默认值。

至于你的第二个例子,我很确定泛型类型不允许使用扩展方法,但是我没有方便的链接

答案 1 :(得分:2)

我迟到了 - 抱歉。

如前所述 - 目前无法将 Func 切换为out参数。

我会抛弃另一种方法来做这种事情 - 它看起来有点流畅的语法,但它的工作原理。

 public class ActionWrap<T>
{
    private readonly Action<T> _action;

    internal ActionWrap(Action<T> action)
    {
        _action = action;
    }

    public Func<T, TResult> OfType<TResult>()
    {
        return arg => { _action(arg); return default(TResult); };
    }
}

public static class DelegateExtensions
{
    public static ActionWrap<T> ToFunc<T>(this Action<T> action)
    {
        return new ActionWrap<T>(action);
    }
}

然后调用它:

Action<int> x = z=> Console.WriteLine(z);
Func<int, float> asFunc = x.ToFunc().OfType<float>();

这不是你想要的,但它实现了不再说明int泛型的目标,并且可以扩展到你想要做类似事情的其他情况。