我可以将泛型方法限制为多个接口吗?

时间:2009-07-28 16:21:47

标签: c# generics where

我有一个通用的方法

public static void DoSomething<T>()
{...}

。现在我想限制那个T.

public static void DoSomething<T>() where T: IInterface1
{...}

但我真正想要的是允许多个接口,比如

public static void DoSomething<T>() where T: IInterface1, IInterface2
{...}

但这不起作用。编译器说类似

  

没有从IInterface1到IInterface2的隐式转换

     

没有从IInterface2到IInterface1的隐式转换

我考虑过让这些类实现一个我可以引用的公共接口,但我无法访问这些类。

我有什么可能允许多个接口?

谢谢, 托比

编辑:这就是我想要做的。我正在开发一个Outlook-Add-In。我经常使用下面这段代码。

    public static object GetItemMAPIProperty<T>(AddinExpress.MAPI.ADXMAPIStoreAccessor adxmapiStoreAccessor, object outlookItem, uint property) where T: Outlook.MailItem, Outlook.JournalItem
    {
        AddinExpress.MAPI.MapiItem mapiItem;
        mapiItem = adxmapiStoreAccessor.GetMapiItem(((T)outlookItem));
        return mapiItem != null ? mapiItem.GetProperty(property) : null;
    }

方法GetMapiItem只需要一个对象,只要它是Outlook的一个项目(Journal,Mail,Contact,...)。这就是为什么我限制T.因为它不能是,例如,Outlook.MAPIFolder。

不,我已将方法更改为

    public static object GetItemMAPIProperty<T>(AddinExpress.MAPI.ADXMAPIStoreAccessor adxmapiStoreAccessor, T outlookItem, uint property)
    {
        AddinExpress.MAPI.MapiItem mapiItem;
        mapiItem = adxmapiStoreAccessor.GetMapiItem(((T)outlookItem));
        return mapiItem.GetProperty(property);
    }

但是开发人员(在本例中为I)可以为其提供任何类型,因为GetMapiItem方法接受一个对象。我希望这是有道理的。我不确定它是否适用于该示例,但我认为将泛型方法限制为多个类型(使用OR)可能是一个好主意。

6 个答案:

答案 0 :(得分:3)

让Interface1和Interface2都来自相同的基本接口。例如:

    public static void DoSomething<T>() where T : ICommon
    {
        //...
    }

    public interface IInterface1 : ICommon
    {}

    public interface IInterface2 : ICommon
    { }

    public interface ICommon
    { }

这样做的好处是,每次添加从ICommon继承的新接口时,您都不必不断更新DoSomething()定义。

编辑:如果您无法控制接口,则可以选择几个选项。这是你可以做的一件事......

    protected static class DoSomethingServer<T1> where T1 : class
    {

        //Define your allowed types here
        private static List<Type> AllowedTypes = new List<Type> {
            typeof(IInterface1),
            typeof(IInterface2)
        };

        public static MethodInvoker DoSomething()
        {
            //Perform type check
            if (AllowedTypes.Contains(typeof(T1)))
            {
                return DoSomethingImplementation;
            }
            else
            {
                throw new ApplicationException("Wrong Type");
            }
        }

        private static void DoSomethingImplementation()
        {
            //Actual DoSomething work here
            //This is guaranteed to only be called if <T> is in the allowed type list
        }
    }

使用原样:

DoSomethingServer<IInterface1>.DoSomething();

不幸的是,这会消除编译时类型的安全性,如果尝试输入错误的类型,它只会在运行时爆炸。显然这不太理想。

答案 1 :(得分:3)

这对我来说很好:

interface I1 { int NumberOne { get; set; } }
interface I2 { int NumberTwo { get; set; } }

static void DoSomething<T>(T item) where T:I1,I2
{
    Console.WriteLine(item.NumberOne);
    Console.WriteLine(item.NumberTwo);
}

所以语法看起来很好......也许是引起问题的其他因素。

答案 2 :(得分:2)

如果您的意思是参数可以是I1的实现或I2的实现,并且它们是不相关的类型,那么您不能编写一个方法组(即具有相同方法名称的重载)来处理这两种类型。 / p>

你甚至不能说(从nader借来!):

    interface I1 { int NumberOne { get; set; } }
    interface I2 { int NumberTwo { get; set; } }

    static void DoSomething<T>(T item) where T : I1
    {
        Console.WriteLine(item.NumberOne);
    }

    static void DoSomething<T>(T item) where T : I2
    {
        Console.WriteLine(item.NumberTwo);
    }

    static void DoSomething<T>(T item) where T : I1, I2
    {
        Console.WriteLine(item.NumberOne);
        Console.WriteLine(item.NumberTwo);
    }

这将为编译器提供一种处理所有可能性的方法,而不会产生歧义。但是为了帮助进行版本控制,C#试图避免添加/删除方法会改变另一种方法的适用性的情况。

您需要编写两个具有不同名称的方法来处理这两个接口。

答案 3 :(得分:1)

一种方法是创建一个扩展Interface1和2的附加接口。然后你把这个接口而不是其他2接口。

这是在java中执行此操作的一种方法;如果我没记错的话,这应该也适用于C#

希望有所帮助。

问候,tobi:P

答案 4 :(得分:1)

    public interface IInterfaceBase
    {

    }
    public interface IInterface1 : IInterfaceBase
    {
      ...
    }
    public interface IInterface2 : IInterfaceBase
    {
      ...
    } 

    public static void DoSomething<T>() where T: IInterfaceBase
    {
    }

如果您希望T为IInterface1或IInterface2,请使用上面的代码

答案 5 :(得分:0)

建立Earwicker所说的......名字不是唯一的出路。你也可以改变方法签名......

public interface I1 { int NumberOne { get; set; } }
public interface I2 { int NumberTwo { get; set; } }

public static class EitherInterface
{
    public static void DoSomething<T>(I1 item) where T : I1
    {
        Console.WriteLine("I1 : {0}", item.NumberOne);
    }

    public static void DoSomething<T>(I2 item) where T : I2
    {
        Console.WriteLine("I2 : {0}", item.NumberTwo);
    }
}

当这样测试时:

public class Class12 : I1, I2
{
    public int NumberOne { get; set; }
    public int NumberTwo { get; set; }
}

public class TestClass
{
    public void Test1()
    {
        Class12 z = new Class12();
        EitherInterface.DoSomething<Class12>((I1)z);
        EitherInterface.DoSomething<Class12>((I2)z);
    }
}

产生此输出:

I1 : 0
I2 : 0

这符合向调用者公开单个方法名称的目标,但由于您没有使用参数,因此无法帮助您。