tags:

views:

386

answers:

6

Hello,

I have a generic method

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

. Now I want to restrict that T.

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

But what I really want is allowing multiple interfaces, something like

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

But that doesn't work. Compiler says something like

There's no implicit conversion from IInterface1 to IInterface2

There's no implicit conversion from IInterface2 to IInterface1

I thought about letting the classes implement a common interface which I can refer to but I don't have access to the classes.

What possibilities do I have to allow multiple Interfaces?

Thanks, Tobi

Edit: Here's what I wanted to do. I'm developing an Outlook-Add-In. I use this piece of code below quite often.

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

The method GetMapiItem takes an object as long as it's one of Outlook's items (Journal, Mail, Contact,...). That's why I was restricting T. Because it cannot be, say, Outlook.MAPIFolder.

No I've changed the method to

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

but the developer (In this case I) can give it any Type because the method GetMapiItem accepts an object. I hope that makes sense. I'm not sure if it does for that example but I guess restricting a generic method to multiple Types (with OR) can be a good idea.

+1  A: 

one way is to create an additional interface which extend both, Interface1 and 2. then you put this interface instead of the other 2.

that's one way to do it in java; if i remember correctly this should work as well in C#

hope that helps.

regards, tobi as well :P

Atmocreations
You've got it backwards. This won't work in C#.
Robert Venables
You'll get a compile time error: There is no implicit reference conversion from (IInterface1||IInterface2) to (Name of Interface That Extends Both).
Robert Venables
+3  A: 

Have Interface1 and Interface2 both derive from the same base interface. Ex:

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

    public interface IInterface1 : ICommon
    {}

    public interface IInterface2 : ICommon
    { }

    public interface ICommon
    { }

The benefit of doing it this way is that you don't have to keep updating your DoSomething() definition every time you add a new interface that inherits from ICommon.

Edit: if you don't have control over the interfaces, you have a couple of options. Here's one thing that you could do...

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

Use as such:

DoSomethingServer<IInterface1>.DoSomething();

Unfortunately, this takes away compile time type safety and it will only blow up at runtime if you try feeding in the wrong type. Obviously this is less than ideal.

Robert Venables
it could even be an empty placeholder interface, IStorable
CrazyJugglerDrummer
From the question: "I thought about letting the classes implement a common interface which I can refer to but I don't have access to the classes." Seems unlikely that the interfaces could be altered then.
Daniel Earwicker
+1  A: 

This compiles fine for me:

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

So the syntax seems fine... perhaps its something else that's causing the problem.

Nader Shirazie
Have you tried using the method?
James
Of course this works in C#, it just does what it's supposed to: it defines a method that accepts only an `item` that supports BOTH interfaces.
Daniel Earwicker
Ah, the comment I was replying to has disappeared.
Daniel Earwicker
@Earwicker - That was my comment. I accidentally left it on the wrong answer.
Robert Venables
@Earwicker - you're right. I'm missing the point..., nova wants "OR". You need two separate methods@James - oops. I get your point.
Nader Shirazie
+1  A: 
    public interface IInterfaceBase
    {

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

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

If you want T to be IInterface1 or IInterface2 use the code above

ArsenMkrt
+1 this would do it, if he is just looking for both types to have some items in common.
Maslow
+2  A: 

If you mean that the parameter can be either an implementation of I1 OR an implementation of I2, and they are unrelated types, then you cannot write one method group (i.e. overloads with the same method name) to handle both types.

You can't even say (borrowing from 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);
    }

This would give the compiler a way to deal with every possibility without ambiguity. But to help with versioning, C# tries to avoid situations where adding/removing a method will change the applicability of another method.

You need to write two methods with different names to handle the two interfaces.

Daniel Earwicker
what if DoSomething should not take parameter?
ArsenMkrt
Makes no difference. You need write a method for each interface, and give the methods different names.
Daniel Earwicker
OR inheritance has a work around with the upcoming duck typing and C# 4.0
Maslow
+1 "You need to write two methods with different names to handle the two interfaces."
Nader Shirazie
A: 

Building off of what Earwicker said... names aren't the only way to go. You could also vary the method signatures...

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

Which when tested like this:

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

Yields this output:

I1 : 0
I2 : 0

This meets the goal of exposing a single method name to the caller, but doesn't help you since you aren't using parameters.

David B
I could do that but I would have to write an implementation for each Interface. That's why I wanted to use generics in the first place.
Tobias