2009-07-28 12 views
5

mam metoda rodzajowaCzy mogę ograniczyć metodę ogólną do więcej niż jednego interfejsu?

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

. Teraz chcę ograniczać że T.

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

Ale to, co naprawdę chcę jest umożliwienie wielu interfejsów, coś

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

Ale to nie działa. Kompilator mówi coś

There's no implicit conversion from IInterface1 to IInterface2

There's no implicit conversion from IInterface2 to IInterface1

myślałem o pozwalając klas wdrożyć wspólny interfejs, który można odnieść do ale nie mam dostępu do tych klas.

Jakie możliwości mam dla wielu interfejsów?

Dzięki, Tobi

Edit: Oto, co chciałem zrobić. Opracowuję dodatek do programu Outlook. Często używam tego fragmentu kodu.

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

Sposób GetMapiItem przyjmuje obiekt tak długo, jak jest to jeden z elementów programu Outlook (Journal, poczta, kontakt, ...). Właśnie dlatego ograniczałem T. Ponieważ nie może być, powiedzmy, Outlook.MAPIFolder.

Nie zmieniłem metodę

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

ale deweloper (w tym przypadku) może dać każdy typ ponieważ metoda GetMapiItem akceptuje obiekt. Mam nadzieję, że ma to sens. Nie jestem pewien, czy robi to dla tego przykładu, ale myślę, że ograniczenie ogólnej metody do wielu typów (z OR) może być dobrym pomysłem.

+0

Jakie ramy są kierowane i czy kompilujesz je za pomocą Visual Studio, a którą wersję? Mam tutaj VS 2008 z .NET 3.5 i powyższe kompiluje się dobrze. Czy jesteś pewien, że to, co dajesz, jest przykładem. –

+1

ponieważ w ten sposób mówisz kompilator, że T powinno być IInterface1 i IInterface2 nie lub –

+0

kod, który on dostarcza działa, on po prostu trzeba powiedzieć, aby przyjąć parametr typu IInterface1 LUB IInterface2 –

Odpowiedz

1

jednym ze sposobów jest utworzenie dodatkowego interfejsu, który wydłuża, Interface1 i 2. Następnie umieścić ten interfejs zamiast drugiej 2.

To jeden ze sposobów, aby to zrobić w Javie; jeśli dobrze pamiętam, to powinno działać również w C#

nadzieję, że pomaga.

pozdrowienia Tobi jak dobrze: P

+0

Masz go od tyłu. To nie zadziała w języku C#. –

+0

Otrzymasz błąd czasu kompilacji: Nie ma żadnej niejawnej konwersji odniesienia z (IInterface1 || IInterface2) do (Nazwa interfejsu, który rozszerza obie). –

3

Have Interface1 i Interface2 obie wywodzą się z tego samego interfejsu bazowego. Ex:

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

    public interface IInterface1 : ICommon 
    {} 

    public interface IInterface2 : ICommon 
    { } 

    public interface ICommon 
    { } 

Zaletą robi to w ten sposób jest to, że nie trzeba, aby utrzymać aktualizacją DoSomething() Definicja za każdym razem dodać nowy interfejs, który dziedziczy ICommon.

Edycja: jeśli nie masz kontroli nad interfejsami, masz kilka opcji. Oto jedna rzecz, którą możesz zrobić ...

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

Zastosowanie takich jak:

DoSomethingServer<IInterface1>.DoSomething(); 

Niestety, ten gładzi skompilować bezpieczeństwa typu czas i będzie to tylko wysadzić w czasie wykonywania przy próbie karmienia w złym typu. Oczywiście jest to mniej niż idealne.

+0

może to być nawet pusty interfejs zastępczy, IStorable –

+1

Z pytania: "Pomyślałem, aby pozwolić klasom wdrożyć wspólny interfejs, do którego mogę się odnieść, ale nie mam dostępu do klas." Wydaje się nieprawdopodobne, aby interfejsy mogły zostać wtedy zmienione. –

2

To kompiluje dobrze dla mnie:

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

więc składnia wydaje się dobrze ... może jej coś innego, co jest przyczyną problemu.

+2

Czy próbowałeś użyć tej metody? – James

+3

Oczywiście działa to w języku C#, po prostu robi to, co powinno: definiuje metodę, która akceptuje tylko "element", który obsługuje oba interfejsy. –

+0

Ah, komentarz, na który odpowiadałem, zniknął. –

1
public interface IInterfaceBase 
    { 

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

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

Jeśli chcesz T być IInterface1 lub IInterface2 użyć kodu powyżej

+0

+1, zrobiłby to, gdyby tylko chciał, aby oba typy miały wspólne cechy. – Maslow

2

Jeśli masz na myśli, że parametr może być implementacja I1 lub wdrożenie I2, a są one niepowiązanych typy, a następnie nie można napisać jednej grupy metod (np. przeciążenia o tej samej nazwie metody), aby obsłużyć oba typy.

Nie można nawet powiedzieć (zapożyczenie z 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); 
    } 

Dałoby to kompilator sposób na radzenie sobie z każdą możliwość bez dwuznaczności. Aby pomóc w wersjonowaniu, C# próbuje uniknąć sytuacji, w których dodanie/usunięcie metody zmieni stosowalność innej metody.

Musisz napisać dwie metody o różnych nazwach, aby obsłużyć dwa interfejsy.

+0

co jeśli DoSomething nie powinien przyjmować parametru? –

+0

Nie ma znaczenia. Musisz napisać metodę dla każdego interfejsu i nadać jej różne nazwy. –

+0

Dziedziczenie OR ma działanie z nadchodzącym pisaniem kaczek i C# 4.0 – Maslow

0

Zbudowanie tego, co powiedział Earwicker ... imiona to nie jedyna droga. Można również zmieniać sygnatur metod ...

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

który podczas testowania tak:

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

Daje to wyjście:

I1 : 0 
I2 : 0 

ten spełnia cel wystawiając jedną metodę nazwa dzwoniącemu, ale nie pomaga, ponieważ nie używasz parametrów.

+0

Mogłem to zrobić, ale musiałbym napisać implementację dla każdego interfejsu. Właśnie dlatego chciałem używać generycznych w pierwszej kolejności. – Tobias