C# 泛型与接口的实际优势

C# 泛型与接口的实际优势,c#,generics,polymorphism,C#,Generics,Polymorphism,在这种情况下,使用泛型与接口的实际优势是什么: void MyMethod(IFoo f) { } void MyMethod<T>(T f) : where T : IFoo { } voidmymethod(ifoof) { } void MyMethod(tf):其中T:IFoo { } 也就是说,在MyMethod中,您可以做哪些在非通用版本中无法做到的事情?我在寻找一个实际的例子,我知道理论上的差异是什么 我知道在MyMethod中,T将是具体的类型,但尽管如此,我

在这种情况下,使用泛型与接口的实际优势是什么:

void MyMethod(IFoo f) 
{
}

void MyMethod<T>(T f) : where T : IFoo
{
}
voidmymethod(ifoof)
{
}
void MyMethod(tf):其中T:IFoo
{
}
也就是说,在
MyMethod
中,您可以做哪些在非通用版本中无法做到的事情?我在寻找一个实际的例子,我知道理论上的差异是什么


我知道在
MyMethod
中,T将是具体的类型,但尽管如此,我只能在方法体中将其用作IFoo。那么什么才是真正的优势呢?

泛型版本允许您使用任何类型作为T-出于某种原因,您可以通过使用where子句限制它,而非泛型版本只支持实现IFoo的东西


另一个(可能更好)的问题是-这两个选项等效吗?

接口方法将为您提供类型为
IFoo
f
,而通用版本将为您提供类型
T
,并带有
T
必须实现
IFoo
的约束

第二种方法允许您根据
T
进行某种类型的查找,因为您可以使用具体的类型。

在这种特殊情况下,没有任何好处。通常,您不会在方法级别指定它,而是在类级别指定它。例如:

public interface IFoo {
        void DoSomethingImportant();
    }

    public class MyContainer<T> where T : IFoo {
        public void Add(T something){
            something.DoSomethingImportant();
            AddThisThing(something);
        }

        public T Get() {
            T theThing = GetSomeKindOfThing();
            return theThing;
        }
    }
  • 通过接口调用方法要比直接在具体类型上调用慢
  • 如果实现
    IFoo
    的类型是值类型,则非泛型版本将装箱参数的值,装箱可能会对性能产生负面影响(特别是如果您经常调用此方法)
  • 如果您的方法返回值,则泛型版本可以返回
    T
    而不是
    IFoo
    ,如果您需要在结果上调用T的方法,这非常方便

    • 做这样的事情更容易:

      void MyMethod<T>(T f) where T : IFoo, new() {
          var t1 = new T();
          var t2 = default(T);
          // Etc...
      }
      

      正如其他地方提到的,一个优点是,如果您返回一个值,就能够返回特定类型的IFoo类型。但是由于您的问题是关于
      voidmymethod(ifoof)
      ,我想给出一个实际的例子,说明至少一种情况,在这种情况下,使用泛型方法比使用接口更有意义(对我来说)。(是的,我在这方面花了一些时间,但我想尝试一些不同的想法。:D)

      有两块代码,第一块只是泛型方法本身和一些上下文,第二块是示例的完整代码,包括大量注释,其中包括关于此实现与等效非泛型实现之间可能存在差异的注释,以及我在实现时尝试的各种不起作用的东西,以及我所作的各种选择的说明等;博士和所有这些

      概念
      公共类FooChains:Dictionary{}
      //管理我们的食品及其连锁店。非常重要的食物链。
      公共类FoodManager
      {
      私有FooChains myChainList=新FooChains();
      //void MyMethod(tf),其中T:IFoo
      void CopyAndChainFoo(TFoo fromFoo),其中TFoo:IFoo
      {
      TFoo-toFoo;
      试一试{
      //从相同类型的foo创建foo
      toFoo=(TFoo)fromFoo.MakeTyped(efoopts.ForChain);
      }
      捕获(例外情况除外){
      //嘿!那可不是同一种食物!
      抛出新的FooChainTypeMismatch(typeof(TFoo),fromFoo,Ex);
      }
      //链接到fromFoo的特定类型的Foo的列表
      列出类型食物;
      如果(!myChainList.Keys.Contains(fromFoo))
      {
      //那里没有foo!列出一个列表并将它们连接到fromFoo
      typedChain=新列表();
      添加(fromFoo,(IEnumerable)typedChain);
      }
      其他的
      //哦,太好了,链条是存在的,呸!
      typedChain=(List)myChainList[fromFoo];
      //将新的foo添加到连接的foo链中
      类型链添加(toFoo);
      //我们完了!
      }
      }
      
      血淋淋的细节
      使用系统;
      使用System.Collections.Generic;
      使用System.Linq;
      使用系统文本;
      命名空间IFooedYouOnce
      {
      //伊福
      //
      //它的个性是如此具有磁性,它的硬盘被擦除。
      //它可以调试其他代码…通过实际调试其他代码。
      //它可以用C语言说哈斯克尔语。
      //
      //它是世界上最有趣的界面。
      公共接口IFoo
      {       
      //虽然最终没有使用这个,但它仍然存在,因为有些
      //没有它,支持派生类的方法看起来很愚蠢。
      布尔坎查因{get;}
      字符串标识符{get;}
      //希望在派生方法中对此设置约束
      //以确保类型安全,但必须使用异常。
      //利斯科夫·亚达·亚达·亚达。。。
      IFoo-MakeTyped(efoopts-fooOpts);
      }
      //在这里使用IEnumerable来利用协方差;
      //我们可以有派生的foo列表,只需回溯和
      //forth用于添加或如果需要使用派生接口。
      //因为可能会有
      //您可以在链集合上作为
      //这样就有了一个位置,而不是说,
      //在FooManager中实现这一切
      公共类FooChains:字典{}
      //管理食物。非常重要的食物。
      公共类FoodManager
      {
      私有FooChains myChainList=新FooChains();
      //可能会在此处添加一个新的()约束,以使
      //创建更容易一点;可以删除整个MakeTypted
      //方法,但试图从
      //这个问题。
      void CopyAndChainFoo(TFoo fromFoo),其中TFoo:IFoo
      //void MyMethod(tf),其中T:IFoo
      {
      TFoo-toFoo;
      
      Bar b = (Bar) m.Get();
      
      void MyMethod<T>(T f) where T : IFoo, new() {
          var t1 = new T();
          var t2 = default(T);
          // Etc...
      }
      
      interface IFoo {
      }
      
      interface IBar {
      }
      
      class FooBar : IFoo, IBar {
      }
      
      void MyMethod<T>(T f) where T : IFoo, IBar {
      }
      
      void Test() {
          FooBar fb = new FooBar();
          MyMethod(fb);
      }
      
      interface IFoo {
      }
      
      interface IBar {
      }
      
      interface IFooBar : IFoo, IBar {
      }
      
      class FooBar : IFooBar {
      }
      
      void MyMethod(IFooBar f) {
      }
      
      void Test() {
          FooBar fb = new FooBar();
          MyMethod(fb);
      }
      
          public class FooChains : Dictionary<IFoo, IEnumerable<IFoo>> { }
      
          // to manage our foos and their chains. very important foo chains.
          public class FooManager
          {
              private FooChains myChainList = new FooChains();
      
              // void MyMethod<T>(T f) where T : IFoo
              void CopyAndChainFoo<TFoo>(TFoo fromFoo) where TFoo : IFoo
              {
                  TFoo toFoo;
      
                  try {
                      // create a foo from the same type of foo
                      toFoo = (TFoo)fromFoo.MakeTyped<TFoo>(EFooOpts.ForChain);
                  }
                  catch (Exception Ex) {
                      // hey! that wasn't the same type of foo!
                      throw new FooChainTypeMismatch(typeof(TFoo), fromFoo, Ex);
                  }
      
                  // a list of a specific type of foos chained to fromFoo
                  List<TFoo> typedFoos;
      
                  if (!myChainList.Keys.Contains(fromFoo))
                  {
                      // no foos there! make a list and connect them to fromFoo
                      typedChain = new List<TFoo>();
                      myChainList.Add(fromFoo, (IEnumerable<IFoo>)typedChain);
                  }
                  else
                      // oh good, the chain exists, phew!
                      typedChain = (List<TFoo>)myChainList[fromFoo];
      
                  // add the new foo to the connected chain of foos
                  typedChain.Add(toFoo);
      
                  // and we're done!
              }
          }
      
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      
      namespace IFooedYouOnce
      {
          // IFoo
          //
          // It's personality is so magnetic, it's erased hard drives.
          // It can debug other code... by actually debugging other code.
          // It can speak Haskell... in C. 
          //
          // It *is* the most interesting interface in the world.
          public interface IFoo
          {       
              // didn't end up using this but it's still there because some
              // of the supporting derived classes look silly without it.
              bool CanChain { get; }
              string FooIdentifier { get; }
      
              // would like to place constraints on this in derived methods
              // to ensure type safety, but had to use exceptions instead.
              // Liskov yada yada yada...
              IFoo MakeTyped<TFoo>(EFooOpts fooOpts);
          }
      
          // using IEnumerable<IFoo> here to take advantage of covariance;
          // we can have lists of derived foos and just cast back and 
          // forth for adding or if we need to use the derived interfaces.
      
          // made it into a separate class because probably there will be
          // specific operations you can do on the chain collection as a
          // whole so this way there's a spot for it instead of, say, 
          // implementing it all in the FooManager
          public class FooChains : Dictionary<IFoo, IEnumerable<IFoo>> { }
      
          // manages the foos. very highly important foos.
          public class FooManager
          {
              private FooChains myChainList = new FooChains();
      
              // would perhaps add a new() constraint here to make the 
              // creation a little easier; could drop the whole MakeTyped
              // method.  but was trying to stick with the interface from
              // the question.
              void CopyAndChainFoo<TFoo>(TFoo fromFoo) where TFoo : IFoo
              // void MyMethod<T>(T f) where T : IFoo
              {
                  TFoo toFoo;
      
                  // without generics, I would probably create a factory
                  // method on one of the base classes that could return
                  // any type, and pass in a type. other ways are possible,
                  // for instance, having a method which took two IFoos, 
                  // fromFoo and toFoo, and handling the Copy elsewhere.
      
                  // could have bypassed this try/catch altogether because
                  // MakeTyped functions throw if the types are not equal,
                  // but wanted to make it explicit here. also, this gives
                  // a more descriptive error which, in general, I prefer
                  try
                  {
                      // MakeTyped<TFoo> was a solution to allowing each TFoo
                      // to be in charge of creating its own objects
                      toFoo = 
                          (TFoo)fromFoo.MakeTyped<TFoo>(EFooOpts.ForChain);
                  }
                  catch (Exception Ex) {
                      // tried to eliminate the need for this try/catch, but
                      // didn't manage. can't constrain the derived classes'
                      // MakeTyped functions on their own types, and didn't
                      // want to change the constraints to new() as mentioned
                      throw 
                          new FooChainTypeMismatch(typeof(TFoo), fromFoo, Ex);
                  }
      
                  // a list of specific type foos to hold the chain
                  List<TFoo> typedFoos;
      
                  if (!myChainList.Keys.Contains(fromFoo))
                  {
                      // we just create a new one and link it to the fromFoo
                      // if none already exists
                      typedFoos = new List<TFoo>();
                      myChainList.Add(fromFoo, (IEnumerable<IFoo>)typedFoos);
                  }
                  else
                      // otherwise get the existing one; we are using the 
                      // IEnumerable to hold actual List<TFoos> so we can just
                      // cast here.
                      typedFoos = (List<TFoo>)myChainList[fromFoo];
      
                  // add it in!
                  typedFoos.Add(toFoo);
              }
          }
      
          [Flags]
          public enum EFooOpts
          {
              ForChain   = 0x01,
              FullDup    = 0x02,
              RawCopy    = 0x04,
              Specialize = 0x08
          }
      
          // base class, originally so we could have the chainable/
          // non chainable distinction but that turned out to be 
          // fairly pointless since I didn't use it. so, just left
          // it like it was anyway so I didn't have to rework all 
          // the classes again.
          public abstract class FooBase : IFoo
          {
              public string FooIdentifier { get; protected set; }
              public abstract bool CanChain { get; }
              public abstract IFoo MakeTyped<TFoo>(EFooOpts parOpts);
          }
      
          public abstract class NonChainableFoo : FooBase
          {
              public override bool CanChain { get { return false; } }
          }
      
          public abstract class ChainableFoo : FooBase
          {
              public override bool CanChain { get { return true; } }
          }
      
          // not much more interesting to see here; the MakeTyped would
          // have been nicer not to exist, but that would have required
          // a new() constraint on the chains function.  
          //
          // or would have added "where TFoo : MarkIFoo" type constraint
          // on the derived classes' implementation of it, but that's not 
          // allowed due to the fact that the constraints have to derive
          // from the base method, which had to exist on the abstract 
          // classes to implement IFoo.
          public class MarkIFoo : NonChainableFoo
          {
              public MarkIFoo()
                  { FooIdentifier = "MI_-" + Guid.NewGuid().ToString(); }
      
              public override IFoo MakeTyped<TFoo>(EFooOpts fooOpts) 
              {
                  if (typeof(TFoo) != typeof(MarkIFoo))
                      throw new FooCopyTypeMismatch(typeof(TFoo), this, null);
      
                  return new MarkIFoo(this, fooOpts);
              }
      
              private MarkIFoo(MarkIFoo fromFoo, EFooOpts parOpts) :
                  this() { /* copy MarkOne foo here */ }
          }
      
          public class MarkIIFoo : ChainableFoo
          {
              public MarkIIFoo()
                  { FooIdentifier = "MII-" + Guid.NewGuid().ToString(); }
      
              public override IFoo MakeTyped<TFoo>(EFooOpts fooOpts)
              {
                  if (typeof(TFoo) != typeof(MarkIIFoo))
                      throw new FooCopyTypeMismatch(typeof(TFoo), this, null);
      
                  return new MarkIIFoo(this, fooOpts);
              }
      
              private MarkIIFoo(MarkIIFoo fromFoo, EFooOpts parOpts) :
                  this() { /* copy MarkTwo foo here */ }
          }
      
          // yep, really, that's about all. 
          public class FooException : Exception
          {
              public Tuple<string, object>[] itemDetail { get; private set; }
      
              public FooException(
                  string message, Exception inner,
                  params Tuple<string, object>[] parItemDetail
              ) : base(message, inner)
              {
                  itemDetail = parItemDetail;
              }
      
              public FooException(
                  string msg, object srcItem, object destType, Exception inner
              ) : this(msg, inner,
                  Tuple.Create("src", srcItem), Tuple.Create("dtype", destType)
              ) { }
          }
      
          public class FooCopyTypeMismatch : FooException
          {
              public FooCopyTypeMismatch(
                  Type reqDestType, IFoo reqFromFoo, Exception inner
              ) : base("copy type mismatch", reqFromFoo, reqDestType, inner)
              { }
          }
      
          public class FooChainTypeMismatch : FooException
          {
              public FooChainTypeMismatch(
                  Type reqDestType, IFoo reqFromFoo, Exception inner
              ) : base("chain type mismatch", reqFromFoo, reqDestType, inner)
              { }
          }
      }
      
      // I(Foo) shot J.R.!
      
      class MyClass : IDisposable {
      
           public void Dispose() {
               if (m_field1 != null) {
                   m_field1.Dispose();
                   m_field1 = null;
               }
               if (m_field2 != null) {
                   m_field2.Dispose();
                   m_field2 = null;
               }
               // etc
           }
      }
      
      class MyClass : IDisposable {
      
          static void IfNotNullDispose(ref IDisposable disposable) {
              if (disposable != null) {
                  disposable.Dispose();
                  disposable = null;
              }
          }
      
          public void Dispose() {
               IfNotNullDispose(ref m_field1);
               IfNotNullDispose(ref m_field2);
               // etc
          }
      }
      
      static void IfNotNullDispose<T>(ref T disposable) where T: class, IDisposable {
          if (disposable != null) {
              disposable.Dispose();
              disposable = null;
          }
      }
      
      using System;
      using System.Diagnostics;
      
      namespace test
      
      {
          class MainApp
          {
              static void Main()
              {
                  Foo f = new Foo();
                  IFoo f2 = f;
      
                  // JIT warm-up
                  f.Bar();
                  f2.Bar();
      
                  int N = 10000000;
                  Stopwatch sw = new Stopwatch();
      
                  sw.Start();
                  for (int i = 0; i < N; i++)
                  {
                      f2.Bar();
                  }
                  sw.Stop();
                  Console.WriteLine("Through interface: {0:F2}", sw.Elapsed.TotalMilliseconds);
      
                  sw.Reset();
      
                  sw.Start();
                  for (int i = 0; i < N; i++)
                  {
                      f.Bar();
                  }
                  sw.Stop();
                  Console.WriteLine("Direct call: {0:F2}", sw.Elapsed.TotalMilliseconds);
      
                  Console.Read();
      
              }
      
              interface IFoo
              {
                  void Bar();
              }
      
              class Foo : IFoo
              {
                  public virtual void Bar()
                  {
                  }
              }
          }
      }