简体   繁体   English

重载C#中的通用接口参数

[英]Overloading generic interface parameters in C#

I have created the following class: 我创建了以下课程:

class GenericTest
{
    public T Do<T>( T test ) where T : class
    {
        return test;
    }

    public IEnumerable<T> Do<T>( List<T> test ) where T : class
    {
        return test;
    }

    public IEnumerable<T> Do<T>( IEnumerable<T> test ) where T : class
    {
        return test;
    }

}

This has three overloads of the Do() function. 这具有Do()函数的三个重载。 I'm trying to understand how the method parameter matching works in C# for generics, especially around interface parameters. 我试图了解方法参数匹配如何在C#中用于泛型,尤其是在接口参数周围。 So, I have the following test program: 因此,我有以下测试程序:

static void Main( string[] args )
{
    GenericTest testing = new GenericTest();

    string s = "TEST";

    List<string> list = new List<string> {s};

    Stack<string> stack = new Stack<string>();
    stack.Push( s );

    testing.Do( s );  //calls public T Do<T>( T test ) 
    testing.Do( list ); //calls IEnumerable<T> Do<T>( List<T> test )
    testing.Do( stack ); //calls public T Do<T>( T test ) where T : class

}

The first call to Do() works as I expected, then the concrete class List parameter matches nicely against the List parameter method, but when I pass an IEnumerable, the compiler doesn't use the IEnumerable parameter method, instead it chooses the generic T method. 对Do()的第一次调用按我的预期工作,然后具体的类List参数与List参数方法很好地匹配,但是当我传递IEnumerable时,编译器不使用IEnumerable参数方法,而是选择了通用T方法。 Is this expected behaviour? 这是预期的行为吗? Can I not overload with just an interface parameter in a generic? 我不能仅在泛型中仅使用接口参数来重载吗?

I'm not sure if first call works as expected since string is IEnumerable<char> ... Which method should be executed in such case? 由于stringIEnumerable<char>我不确定第一次调用是否按预期方式工作...在这种情况下应执行哪个方法? It depends on your particular requirement. 这取决于您的特定要求。

Behaviour that you are describing may be flexibly implemented following CoR pattern where you define matching logic and chain elements order according your particular needs. 您描述的行为可以按照CoR模式灵活地实现,在CoR模式中,您可以根据特定需要定义匹配逻辑和链元素顺序。

Below is just an illustration that shows the idea(I'm sure it can be refactored): 下面只是一个展示想法的图示(我敢肯定它可以重构):

public abstract class ChainElem
{
    public abstract bool IsMatching(object o);
    public abstract void Do(object o);
}

public class ChainElemIList : ChainElem{
    public override bool IsMatching(object o) {
        //Matches IList implementations only.
        if( o is IList )
            return true; 
        else
            return false;
    }

    public override void Do(object o) {
        //Do something with the IList
        Console.WriteLine("processing IList...");
    }
}

public class ChainElemIEnumerable : ChainElem{
    public override bool IsMatching(object o) {
        //Matches all IEnumerable implementations(but not string).
        //This is something that you won't achieve with generics.
        if( o is IEnumerable && !(o is string) )
            return true; 
        else
            return false;
    }

    public override void Do(object o) {
        //Do something with the IEnumerable(but not string)
        Console.WriteLine("processing IEnumerable(but not string)...");
    }
}

public class ChainElemString : ChainElem{
    public override bool IsMatching(object o) {
        //Matches strings only.
        if( o is string )
            return true; 
        else
            return false;
    }

    public override void Do(object o) {
        //Do something with the string
        Console.WriteLine("processing string...");
    }
}

public class ChainElemObject : ChainElem{
    public override bool IsMatching(object o) {
        //Matches everything else.  
        return true; 
    }

    public override void Do(object o) {
        //Do something with the object
        Console.WriteLine("processing object...");
    }
}

void Main()
{
    string s = "TEST";
    List<string> list = new List<string> {s};
    Stack<string> stack = new Stack<string>();
    stack.Push( s );
    object o = new object();

    //construct the chain - order is important and depends on your requirements
    var chain = new List<ChainElem> {
        new ChainElemIList(),
        new ChainElemIEnumerable(),
        new ChainElemString(),
        new ChainElemObject()
    };

    //processing
    chain.First(c => c.IsMatching(list)).Do(list);
    chain.First(c => c.IsMatching(stack)).Do(stack);
    chain.First(c => c.IsMatching(s)).Do(s);
    chain.First(c => c.IsMatching(o)).Do(o);

    //OUTPUT:
    //  processing IList...
    //  processing IEnumerable(but not string)...
    //  processing string...
    //  processing object...

}

So, it seems that you can't overload with a interface parameter if you have generic method that matches the signature. 因此,如果您具有与签名匹配的通用方法,则似乎无法重载接口参数。

Either use a different name for your methods, or a concrete class in the parameter list. 为您的方法使用其他名称,或者在参数列表中使用具体的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM