泛型与接口的实际优势 [英] Practical advantage of generics vs interfaces

查看:24
本文介绍了泛型与接口的实际优势的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

即您可以在 MyMethod 中做什么而在非通用版本中无法做到?我正在寻找一个实际的例子,我知道理论上的差异是什么.

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.

我知道在 MyMethod 中,T 将是具体类型,但我只能将它用作方法主体内的 IFoo.那么什么是真正的优势?

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?

推荐答案

嗯,在别处提到的一个优点是,如果您返回一个值,将能够返回特定类型的 IFoo 类型.但是由于您的问题特别是关于 void MyMethod(IFoo f),我想举一个现实的例子来说明至少一种情况,在这种情况下,使用泛型方法比接口更有意义(对我来说).(是的,我花了一些时间在这上面,但我想尝试一些不同的想法.:D)

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)

有两个代码块,第一个是泛型​​方法本身和一些上下文,第二个是示例的完整代码,包括许多注释,从注释与等效的非泛型之间的可能差异实施,以及我在实施过程中尝试过但没有奏效的各种事情,以及我所做的各种选择的注释等.TL;DR 等等.

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.

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

这篇关于泛型与接口的实际优势的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆