2009-07-28 12 views
5

Ho un metodo genericoPosso limitare il metodo generico a più di una interfaccia?

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

. Ora voglio limitare tale T.

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

Ma quello che voglio realmente sta permettendo più interfacce, qualcosa come

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

Ma questo non funziona. Compiler dice qualcosa come

There's no implicit conversion from IInterface1 to IInterface2

There's no implicit conversion from IInterface2 to IInterface1

ho pensato di lasciare che le classi implementare un'interfaccia comune che posso fare riferimento, ma non hanno accesso alle classi.

Quali possibilità devo consentire più interfacce?

Grazie, Tobi

Edit: Ecco cosa volevo fare. Sto sviluppando un componente aggiuntivo di Outlook. Io uso questo codice sotto molto spesso.

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

Il metodo GetMapiItem prende un oggetto finché è uno degli elementi di Outlook (Journal, Mail, contatti, ...). Ecco perché stavo limitando T. Perché non può essere, per esempio, Outlook.MAPIFolder.

No ho cambiato il metodo per

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

ma lo sviluppatore (in questo caso I) può dare qualsiasi tipo perché il metodo GetMapiItem accetta un oggetto. Spero che abbia un senso. Non sono sicuro che lo faccia per quell'esempio, ma suppongo che limitare un metodo generico a più tipi (con OR) possa essere una buona idea.

+0

Quale framework si sta targeting e si sta compilando con Visual Studio e quale versione? Ho qui VS 2008 con .NET 3.5 e le compilazioni di cui sopra va bene. Sei sicuro che quello che ci dai come esempio è quello che hai. –

+1

perché in questo modo si dice al compilatore che T dovrebbe essere IInterface1 e IInterface2 not oppure –

+0

il codice che fornisce funziona, ha solo bisogno di dire il metodo per prendere i parametri di tipo IInterface1 O IInterface2 –

risposta

1

un modo è quello di creare un'interfaccia aggiuntiva che si estendono sia, Interface1 e 2. poi si mette questa interfaccia anziché l'altro 2.

che è un modo per farlo in Java; se ricordo bene dovrebbe funzionare anche in C#

speranza che aiuti.

saluti, tobi così: P

+0

Hai fatto all'indietro. Questo non funzionerà in C#. –

+0

Viene visualizzato un errore in fase di compilazione: non esiste alcuna conversione implicita del riferimento da (IInterface1 || IInterface2) a (Nome dell'interfaccia che estende entrambi). –

3

Avere Interface1 e Interface2 entrambi derivano dalla stessa interfaccia di base. Es:

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

    public interface IInterface1 : ICommon 
    {} 

    public interface IInterface2 : ICommon 
    { } 

    public interface ICommon 
    { } 

Il vantaggio di farlo in questo modo è che non c'è bisogno di tenere aggiornata la vostra DoSomething() Definizione ogni volta che si aggiunge una nuova interfaccia che eredita da ICommon.

Modifica: se non si ha il controllo sulle interfacce, ci sono un paio di opzioni. Ecco una cosa che potresti fare ...

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

Usa come ad esempio:

DoSomethingServer<IInterface1>.DoSomething(); 

Purtroppo, questo toglie compilazione tempo di sicurezza tipo e sarà solo saltare in aria in fase di esecuzione se si tenta l'alimentazione nel tipo sbagliato. Ovviamente questo non è l'ideale.

+0

potrebbe anche essere un'interfaccia segnaposto vuota, IS2able –

+1

Dalla domanda: "Ho pensato di lasciare che le classi implementassero un'interfaccia comune a cui posso riferirmi ma non ho accesso alle classi." Sembra improbabile che le interfacce possano essere modificate. –

2

Questo compila bene per 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); 
} 

Così la sintassi sembra che vada bene ... forse la sua qualcos'altro che sta causando il problema.

+2

Hai provato a utilizzare il metodo? – James

+3

Ovviamente questo funziona in C#, fa solo ciò che è supposto: definisce un metodo che accetta solo un 'item' che supporti le interfacce BOTH. –

+0

Ah, il commento a cui stavo rispondendo è scomparso. –

1
public interface IInterfaceBase 
    { 

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

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

Se si desidera T essere IInterface1 o IInterface2 utilizzare il codice di cui sopra

+0

+1 farebbe questo, se sta solo cercando entrambi i tipi per avere alcuni elementi in comune. – Maslow

2

Se vuoi dire che il parametro può essere sia un'implementazione di I1 o un'implementazione di I2, e sono i tipi non correlati, quindi non è possibile scrivere un gruppo di metodi (es. overload con lo stesso nome del metodo) per gestire entrambi i tipi.

non si può neanche dire (prestito da 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); 
    } 

Questo darebbe al compilatore un modo per trattare con tutte le possibilità senza ambiguità. Ma per aiutare con il controllo delle versioni, C# cerca di evitare situazioni in cui l'aggiunta/rimozione di un metodo cambierà l'applicabilità di un altro metodo.

È necessario scrivere due metodi con nomi diversi per gestire le due interfacce.

+0

cosa fare se DoSomething non deve prendere i parametri? –

+0

Non fa differenza. È necessario scrivere un metodo per ogni interfaccia e fornire i metodi con nomi diversi. –

+0

O l'ereditarietà ha un problema con la digitazione anatra imminente e C# 4.0 – Maslow

0

Costruire su quello che Earwicker ha detto ... i nomi non sono l'unica strada da percorrere. Si potrebbe anche variare le firme dei metodi ...

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

che durante il test in questo modo:

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

rese questa uscita:

I1 : 0 
I2 : 0 

Questo soddisfa l'obiettivo di esporre un unico metodo nome al chiamante, ma non ti aiuta dato che non stai usando i parametri.

+0

Potrei farlo ma dovrei scrivere un'implementazione per ogni interfaccia. Ecco perché volevo usare i generici in primo luogo. – Tobias