2009-07-28 14 views
5

Tengo un método genérico¿Puedo restringir el método genérico a más de una interfaz?

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

. Ahora quiero para restringir que T.

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

Pero lo que realmente quiero es que permite múltiples interfaces, algo así como

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

Pero eso no funciona. Compilador dice algo así como

There's no implicit conversion from IInterface1 to IInterface2

There's no implicit conversion from IInterface2 to IInterface1

pensé en dejar que las clases de implementar una interfaz común que puedo hacer referencia a pero no tienen acceso a las clases.

¿Qué posibilidades tengo de permitir múltiples interfaces?

Gracias, Tobi

Editar: Esto es lo que quería hacer. Estoy desarrollando un Outlook-Add-In. Utilizo este fragmento de código a continuación bastante a menudo.

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

El método GetMapiItem toma un objeto con tal de que uno de los elementos de Outlook (Diario, Mail, contactos, ...). Es por eso que estaba restringiendo a T. Porque no puede ser, por ejemplo, Outlook.MAPIFolder.

No he cambiado el método para

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

pero el desarrollador (En este caso yo) puede darle cualquier tipo porque el método GetMapiItem acepta un objeto. Espero que tenga sentido. No estoy seguro de que sirva para ese ejemplo, pero supongo que restringir un método genérico a varios tipos (con O) puede ser una buena idea.

+0

¿A qué marco se dirige, y está compilando con Visual Studio, y qué versión? Tengo aquí VS 2008 con .NET 3.5 y lo anterior compila muy bien. ¿Estás seguro de que lo que nos estás dando como ejemplo es lo que tienes? –

+1

porque de esa manera usted dice que el compilador que T debe ser IInterface1 e IInterface2 not o –

+0

el código que proporciona funciona, solo necesita decir el método para tomar el parámetro de tipo IInterface1 O IInterface2 –

Respuesta

1

una forma es crear una interfaz adicional que se extienden tanto, Interface1 y 2. A continuación, se pone esta interfaz en lugar de los otros 2.

que es una manera de hacerlo en Java; si recuerdo correctamente esto debería funcionar también en C#

espero que ayude.

cordiales, TOBI así: P

+0

Lo tienes al revés. Esto no funcionará en C#. –

+0

Obtendrá un error de tiempo de compilación: no hay una conversión de referencia implícita de (IInterface1 || IInterface2) a (Nombre de la interfaz que extiende ambos). –

3

Have Interface1 y Interface2 tanto derivan de la misma interfaz de base. Ej:

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

    public interface IInterface1 : ICommon 
    {} 

    public interface IInterface2 : ICommon 
    { } 

    public interface ICommon 
    { } 

La ventaja de hacerlo de esta manera es que usted no tiene que mantener la actualización de su HacerAlgo() definición cada vez que se agrega una nueva interfaz que hereda de ICommon.

Editar: si no tiene control sobre las interfaces, tiene un par de opciones. Aquí hay una cosa que podrías hacer ...

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

uso como tal:

DoSomethingServer<IInterface1>.DoSomething(); 

Desafortunadamente, esto le quita compilar la seguridad de tipos tiempo y que sólo va a explotar en tiempo de ejecución si se intenta alimentar el tipo incorrecto. Obviamente esto es menos que ideal.

+0

incluso podría ser una interfaz de marcador de posición vacía, IStorable –

+1

A partir de la pregunta: "Pensé en dejar que las clases implementaran una interfaz común a la que pueda hacer referencia, pero no tengo acceso a las clases". Parece poco probable que las interfaces puedan ser alteradas en ese momento. –

2

Esto compila bien para mí:

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

Así que la sintaxis parece estar bien ... tal vez su algo más que está causando el problema.

+2

¿Has probado con el método? – James

+3

Por supuesto, esto funciona en C#, simplemente hace lo que se supone que debe: define un método que acepta solo un 'elemento' que admite AMBAS interfaces. –

+0

Ah, el comentario al que estaba respondiendo ha desaparecido. –

1
public interface IInterfaceBase 
    { 

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

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

Si desea T para ser IInterface1 o IInterface2 utilizan el código anterior

+0

+1 esto lo haría, si solo está buscando ambos tipos para tener algunos elementos en común. – Maslow

2

Si se refiere a que el parámetro puede ser una implementación de I1 O una implementación de I2, y son tipos no relacionados, a continuación, no puede escribir un grupo de métodos (es decir, sobrecargas con el mismo nombre de método) para manejar ambos tipos.

Incluso no se puede decir (préstamos de 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); 
    } 

Esto daría al compilador de una manera de hacer frente a todas las posibilidades y sin ambigüedad. Pero para ayudar con el control de versiones, C# intenta evitar situaciones en las que agregar/eliminar un método cambiará la aplicabilidad de otro método.

Necesita escribir dos métodos con diferentes nombres para manejar las dos interfaces.

+0

¿Qué pasa si DoSomething no debe tomar el parámetro? –

+0

Hace la diferencia. Necesita escribir un método para cada interfaz y darles diferentes nombres. –

+0

O la herencia tiene una solución alternativa con la siguiente tipificación de pato y C# 4.0 – Maslow

0

Aprovechando lo que dijo Earwicker ... los nombres no son el único camino a seguir. También podría variar las firmas de método ...

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

Qué cuando se prueba como esta:

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

Rendimientos esta salida:

I1 : 0 
I2 : 0 

Este se encuentra con el objetivo de exponer un método único nombre a la persona que llama, pero no lo ayuda, ya que no está usando parámetros.

+0

Podría hacerlo, pero tendría que escribir una implementación para cada interfaz. Es por eso que quería usar genéricos en primer lugar. – Tobias

Cuestiones relacionadas