泛型与接口的实际优势

发布于 2024-12-01 17:34:58 字数 315 浏览 0 评论 0原文

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

void MyMethod(IFoo f) 
{
}

void MyMethod<T>(T f) : where T : IFoo
{
}

即,您可以在 MyMethod 中执行哪些在非泛型版本中无法执行的操作?我正在寻找一个实际的例子,我知道理论上的差异是什么。

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

What would be a practical advantage of using generics vs interfaces in this case:

void MyMethod(IFoo f) 
{
}

void MyMethod<T>(T f) : where T : IFoo
{
}

I.e. what can you do in MyMethod<T> that you couldn't in the non-generic version? I'm looking for a practical example, I know what the theoretical differences are.

I know that in MyMethod<T>, T will be the concrete type, but nonetheless I will only be able to use it as an IFoo within the body of the method. So what would be a real advantage?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

枯寂 2024-12-08 17:34:58
  • 通过接口调用方法比直接在具体类型上调用方法要慢
  • 如果实现 IFoo 的类型是值类型,非泛型版本将对参数的值进行装箱,装箱可能会产生负面影响影响性能(特别是如果您经常调用此方法)
  • 如果您的方法返回一个值,通用版本可以返回一个 T 而不是 IFoo,如果您需要对结果调用 T 的方法
  • Calling a method through an interface is slower than calling it directly on the concrete type
  • If the type implementing IFoo is a value type, the non-generic version will box the value of the parameter, and boxing can negatively affect performance (especially if you call this method very often)
  • If your method returns a value, the generic version can return a T rather than a IFoo, which is convenient if you need to call a method of T on the result
满地尘埃落定 2024-12-08 17:34:58

好吧,正如其他地方提到的,一个优点是如果返回一个值,则能够返回特定类型的 IFoo 类型。但由于您的问题具体是关于 void MyMethod(IFoo f) 的,所以我想给出至少一种情况的实际示例,其中使用通用方法(对我来说)比接口更有意义。 (是的,我在这方面花了一些时间,但我想尝试一些不同的想法。:D)

有两个代码块,第一个只是通用方法本身和一些上下文,第二个是完整的代码该示例,包括大量注释,包括关于此示例与等效的非通用实现之间可能存在的差异的注释,以及我在实现时尝试但不起作用的各种操作,以及关于我所做的各种选择的注释等。博士等等。

概念

    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.!

Well, one advantage as mentioned elsewhere, would be the ability to return a specific type of IFoo type if you return a value. But since your question is specifically about void MyMethod(IFoo f), I wanted to give a realistic example of at least one type of situation where using a generic method makes more sense (to me) than the interface. (Yes I spent a bit of time on this, but I wanted to try out some different ideas. :D)

There are two blocks of code, the first is just the generic method itself and some context, the second is the full code for the example, including lots of comments ranging from notes on possible differences between this and an equivalent non-generic implementation, as well as various things I tried while implementing that didn't work, and notes on various choices I made, etc. TL;DR and all that.

Concept

    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!
        }
    }

Gory Details

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.!
桃气十足 2024-12-08 17:34:58

做这样的事情更容易:

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

此外,当您引入更多接口时,泛型对于调用者可能会更加“温和”。例如,您可以从 2 个接口继承一个类并直接传递它,就像这样......

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);
}

而“仅接口”方法将需要一个“中间”接口(IFooBar)。 。

interface IFoo {
}

interface IBar {
}

interface IFooBar : IFoo, IBar {
}

class FooBar : IFooBar {
}

void MyMethod(IFooBar f) {
}

void Test() {
    FooBar fb = new FooBar();
    MyMethod(fb);
}

Doing things like these is easier:

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

Also, as you introduce more interfaces, generics may be more "gentle" to callers. For example, you can inherit a class from 2 interfaces and pass it directly, like this...

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);
}

...while "interface-only" method would require an "intermediary" interface (IFooBar)...

interface IFoo {
}

interface IBar {
}

interface IFooBar : IFoo, IBar {
}

class FooBar : IFooBar {
}

void MyMethod(IFooBar f) {
}

void Test() {
    FooBar fb = new FooBar();
    MyMethod(fb);
}
王权女流氓 2024-12-08 17:34:58

2年后我发现了一个非常简单又有用的案例。考虑这个常见模式:

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
    }
}

不幸的是,这在 C# 中是非法的,因为您不能使用 ref 参数的接口,您必须使用您的具体类型会通过,没有别的。因此,您必须为要处理的每种类型的字段编写不同的方法。哦等等,这正是泛型为您所做的:

static void IfNotNullDispose<T>(ref T disposable) where T: class, IDisposable {
    if (disposable != null) {
        disposable.Dispose();
        disposable = null;
    }
}

现在一切都按预期运行!

2 years later I found a very simple and useful case. Consider this common pattern:

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
     }
}

I've always wanted to write a helper method to avoid having to write all this boilerplate for every field:

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
    }
}

Unfortunately this is illegal in C# because you cannot use an interface for ref parameters, you must use the concrete type you'll pass in and nothing else. So you'd have to write a different method for every single type of field you want to dispose. Oh wait that's exactly what generics do for you:

static void IfNotNullDispose<T>(ref T disposable) where T: class, IDisposable {
    if (disposable != null) {
        disposable.Dispose();
        disposable = null;
    }
}

Now everything works as intended!

怎会甘心 2024-12-08 17:34:58

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

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

The interface method will supply you an f of type IFoo, whereas the generic version will supply you a type T with the constraint that T has to implement IFoo.

The second method would allow you to have some kind of lookup depending on T, as you have a concrete type to work with.

无可置疑 2024-12-08 17:34:58

在这种特定情况下,没有任何好处。一般来说,您不会在方法级别指定这一点,而是在类级别指定。例如,

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;
        }
    }

请注意,我们需要 T 实现 IFoo,因为 Add 方法需要调用 IFoo 实现的 DoSomethingImportantMethod。

但请注意,在 Get 方法中,我们将返回此类的最终用户提供的 T,而不是普通的旧 IFoo,这减轻了开发人员始终强制转换为其实际具体 T 的需要。

示例:

public class Bar : IFoo{
  //....
}

MyContainer<Bar> m = new MyContainer<Bar>();
//stuff happens here
Bar b = m.Get();

请注意,如果我只是返回 IFoo,那么我必须在最后一行执行此操作:

Bar b = (Bar) m.Get();

In this particular case, there is no benefit. In general you wouldn't specify this at a method level, but at a class level. E.g.,

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;
        }
    }

Notice that we require T to implement IFoo because of the Add method where we need to call the DoSomethingImportantMethod implemented by IFoo.

But notice in the Get method that we will return the T provided by end user of this class instead of a plain old IFoo, which alleviates the need for the developer to always cast to their actual concrete T.

Example:

public class Bar : IFoo{
  //....
}

MyContainer<Bar> m = new MyContainer<Bar>();
//stuff happens here
Bar b = m.Get();

Note that if I was just returning an IFoo, then I would have to do this at the last line instead:

Bar b = (Bar) m.Get();
反差帅 2024-12-08 17:34:58

参考上面报告的基准

@Branko,通过接口调用方法实际上比“正常”虚拟方法调用慢...这是一个简单的基准:>pastebin.com/jx3W5zWb – Thomas Levesque 2011 年 8 月 29 日上午 0: 33

运行Visual Studio 2015 中的代码,直接调用和通过接口的结果大致相同:

  • 直接调用:90,51 毫秒; 112.49 毫秒; 81,22 毫秒
  • 通过接口:92,85 毫秒;90,14 毫秒; 88,56 毫秒

用于基准测试的代码(来自 http://pastebin.com/jx3W5zWb )是:

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()
            {
            }
        }
    }
}

referring to the benchmark reported above

@Branko, calling a method through an interface is actually slower than >a "normal" virtual method call... Here's a simple benchmark: >pastebin.com/jx3W5zWb – Thomas Levesque Aug 29 '11 at 0:33

running the code in Visual Studio 2015 the result are roughly equivalent between Direct call and Through interface:

  • Direct call: 90,51 millisec; 112,49 millisec; 81,22 millisec
  • Through interface: 92,85 millisec;90,14 millisec; 88,56 millisec

the code used to benchmark (from http://pastebin.com/jx3W5zWb ) is:

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()
            {
            }
        }
    }
}
2024-12-08 17:34:58

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

另一个(也许更好)问题是 - 这两个选项等效吗?

The generic version allows you to use any type as T - which you for some reason restricted back by using the where clause, whereas your non-generic version supports only something implementing IFoo.

Another (maybe better) question is - are these two options equivalent?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文