【问题标题】:How can I define a generic extension method如何定义通用扩展方法
【发布时间】:2013-07-16 06:40:11
【问题描述】:

我正在尝试定义一个扩展方法,该方法可以返回调用定义的类型的对象。

预期用途: Cat acat = guy.GiveMeYourPet<Cat>();

尝试实施

我可以毫不费力地定义这样的泛型方法:

public static T GiveMeYourPet<T>(Person a) { ... }

Cat acat = GiveMeYourPet<Cat>(guy);

或像这样的扩展方法:

public static Cat GiveMeYourPetCat<P>(this P self) where P : Person, ... { ... }

Cat acat = guy.GiveMeYourPetCat();

但是当我尝试做我真正想做的事时:

public static T GiveMeYourPet<T, P>(this P self) where P : Person, ... { ... }

Cat acat = guy.GiveMeYourPet<Cat>();

编译器期望 GiveMeYourPet() 接收 2 个类型参数(即使一个是通过调用对象 guy 的扩展方法隐式提供的。

我能做些什么来完成这项工作?

请注意,我还尝试颠倒定义参数的顺序,但没有任何变化:

public static T GiveMeYourPet<P, T>(this P self)

以下调用也不起作用,因为您不能在类型规范中进行方法调用:

Cat acat = guy.GiveMeYourPet<guy.GetType(), Cat>();

【问题讨论】:

  • 我认为您需要将 T 作为参数显式传递 - 否则编译器将如何知道要输入什么返回?
  • 您不能只使某些参数隐式。要么全无。
  • 我认为扩展方法不适用于泛型 this 类型;第一个解决方案应该可以工作。
  • 你会在这种方法的实现中投入什么?我认为最好将其拆分为 2 个单独的 1-type 参数化方法。

标签: c#


【解决方案1】:

C# 编译器类型推断并不像您希望的那样复杂。您必须在这种方法中明确指定这两种类型:

void Main()
{
    int i = 0;
    bool b = i.GiveMeYourPet<bool, int>();
}

public static class MyExtensions
{
    public static T GiveMeYourPet<T, P>(this P self)
    {
        return default(T);
    }
}

如果您想避免明确指定两者(我不会责怪您),您可以尝试将您的方法更改为:

public static T GiveMeYourPet<T>(this IPetOwner self)

(有了这个接口,你甚至不需要知道真正的类型是什么;如果你知道,使用asis)甚至:

public static T GiveMeYourPet<T>(this object self)

(并使用asis

如果这不是一个选项,并且guy(在您的示例中)的真实类型不是静态已知的(例如,您只是将他作为object),您可能必须使用反射,例如:

MethodInfo method = typeof(MyExtensions).GetMethod("GiveMeYourPet");
MethodInfo generic = method.MakeGenericMethod(typeof(Pet), guy.GetType());
generic.Invoke(guy, null);

【讨论】:

  • 不仅如此,如果我不知道'guy'的类型(或者本例中的i),我该如何传入this参数的类型?跨度>
  • @Alain dynamic 实际上是一个非常有用的语言特性。看我的回答。
  • 我能够将P 上的类型限制减少到单个界面,并在您的示例public static T GiveMeYourPet&lt;T&gt;(this IPetOwner self) 中使用该技术 - 感谢您的想法。
【解决方案2】:

如果像 guy.GiveMeYour.Pet&lt;Cat&gt;(); 这样的东西可以工作,你可以构建 2 个类似于代码的级别:

public class GiveMeYourBuilder<P>
{
   public P Me {get;set;}
   public T Pet<T>() : where T: new()
   { return new T();}
}

public static PetExtensions 
{
    public GiveMeYourBuilder<P>(this P me)
    {
         return new GiveMeYourBuilder<P> { Me = me;}
    }
}

【讨论】:

    【解决方案3】:

    你不能部分指定泛型参数,要么它们都是推断出来的,要么你必须全部指定它们。在这种情况下,您可以获得的最接近的可能是返回一个中间对象,该对象带有调用扩展方法的通用 Person 类型,并在其上定义您的 Get 方法:

    public class GiveContext<T> where T : Person
    {
        public P MeYourPet<P>() where P : Pet
        {
            return default(P);
        }
    }
    
    public static GiveContext<T> Give<T>(this T person) where T : Person
    {
        return new GiveContext<T>();
    }
    

    你可以像这样使用:

    var p = new Person();
    Cat c = p.Give().MeYourPet<Cat>();
    

    【讨论】:

    • 如果您对Give&lt;T&gt; 有一个Person 约束,不妨在扩展上使用this Person person。我觉得它没有解决任何问题,因为这是原始问题的第一次尝试
    【解决方案4】:

    很遗憾,您不能这样做。如果编译器无法all 找出它们,则需要输入所有类型参数。 C# 编译器并不那么聪明。 dynamic 可以提供帮助:

    public static T GiveMeYourPet<T>(this dynamic self)
    {
        //in here check that self meets your constraints using is, as, etc.
    }
    

    【讨论】:

      猜你喜欢
      • 2016-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-14
      • 2014-10-12
      • 2020-06-07
      • 2012-09-10
      相关资源
      最近更新 更多