2010-07-25 19 views
5

Lo que básicamente deseo hacer es diseñar una interfaz genérica que, cuando se implementa, da como resultado una clase que puede comportarse exactamente como T, excepto que tiene alguna funcionalidad adicional. Aquí está un ejemplo de lo que estoy hablando:Interfaces C#: ¿Es posible hacer referencia al tipo que implementa la interfaz dentro de la interfaz?

public interface ICoolInterface<T> 
{ 
    T Value { get; set; } 
    T DoSomethingCool(); 
} 

public class CoolInt : ICoolInterface<int> 
{ 
    private int _value; 

    public CoolInt(int value) 
    { 
     _value = value; 
    } 

    public int Value 
    { 
     get { return _value; } 
     set { _value = value; } 
    } 

    public int DoSomethingCool() 
    { 
     return _value * _value; 
     // Ok, so that wasn't THAT cool 
    } 
} 

Y esto es todo muy bien, pero con el fin de utilizar CoolInt, tengo que hacer algo como esto:

CoolInt myCoolInt = new CoolInt(5); 
int myInt = myCoolInt.Value; 

I más bien, en términos de asignación al menos, que CoolInt funciona igual que int. En otras palabras:

CoolInt myCoolInt = 5; 
int myInt = myCoolInt; 

Para lograr esto, añaden estos dos operadores de conversión a mi clase CoolInt:

public static implicit operator CoolInt(int val) 
    { 
     return new CoolInt(val); 
    } 

    public static implicit operator int(CoolInt obj) 
    { 
     return obj.Value; 
    } 

Funciona increíblemente. Ahora, preferiría que pudiera agregar estas dos sobrecargas a la interfaz, de modo que los implementadores de la interfaz sean forzados para implementar estos operadores. El problema es que los prototipos de estos operadores se refieren directamente a CoolInt.

C# tiene muchos nombres de "marcadores de posición" para cosas que están implícitamente definidas o que aún no se han definido. La T que se usa convencionalmente en la programación genérica es un ejemplo. Supongo que la palabra clave value, utilizada en Properties, es otra. La referencia "this" podría considerarse otra. Espero que haya otro símbolo que pueda usar en mi interfaz para indicar "el tipo de la clase que está implementando esta interfaz", p. "implementador".

public static implicit operator implementer(int val) 
    { 
     return new IntVal(val); 
    } 

    public static implicit operator int(implementer obj) 
    { 
     return obj.Value; 
    } 

¿Esto es posible?

Respuesta

2

Lamentablemente no :(

C# no hace bien cuando se trata de la sobrecarga de operadores (Este es un ejemplo, otro es las limitaciones genéricas sobre ciertos tipos de operador).

+0

sí, es extraño cómo la sobrecarga del operador a veces no funciona: ¡en el ejemplo genérico abstracto que publiqué aquí parece funcionar de una manera pero no de la otra! –

4

¿Por qué no creas un abstract class? De esta manera puedes construir algunas funcionalidades "predeterminadas" en tu clase.

+1

Creo que eso me lleva a una parte del camino, pero creo que aún me encontraría con un problema. Digamos que tengo AbstractCoolClass, y la versión concreta se llama CoolInt, como antes. Bueno, el operador de conversión implícito de int a CoolInt devolvería una referencia de AbstractCoolClass, que luego tendría que ser convertida explícitamente en un CoolInt. Prefiero prescindir de moldes explícitos, pero parece que no tengo otra opción. –

+0

Una clase abstracta * genérica * no necesitaría devolver un objeto 'AbstractCoolClass' podría devolver una' T'. –

+0

Necesito hacer la conversión en ambos sentidos. Entonces, necesito un operador que acepte una 'T' y devuelva una instancia de mi clase, y otro método que acepte una instancia de mi clase y devuelva una' T'. En ambos casos, necesitaría usar 'AbstractCoolClass' como el tipo de datos para la instancia de mi clase. En el último caso, no es un problema, porque simplemente puedo tener el molde de método sobrecargado de 'AbstractCoolClass' a la clase concreta (por ejemplo,' CoolInt'). Sin embargo, en el primer caso, tengo que devolver un 'AbstractCoolClass' y depende del desarrollador del cliente hacer el reparto. –

0

por qué no utilizar los métodos de extensión en vez ? Eso le permite "agregar" métodos al int sin tener que usar un tipo diferente

0

Esto es probablemente lo más cercano que puede obtener usando un tipo base abstracto, pero lamentablemente incluso esto tiene un problema con uno de los operadores implícitos y tienes que hacer: -

 CoolInt x = (CoolInt)5; 
     int j = x; 

¿Cerrar lo suficiente?

// Slightly sneaky, we pass both the wrapped class and the wrapping class as type parameters to the generic class 
    // allowing it to create instances of either as necessary. 

    public abstract class CoolClass<T, U> 
     where U : CoolClass<T, U>, new() 
    { 
     public T Value { get; private set; } 

     public abstract T DoSomethingCool(); 

     // Non-public constructor 
     protected CoolClass() 
     { 
     } 

     public CoolClass(T value) 
     { 
      Value = value; 
     } 

     public static implicit operator CoolClass<T, U>(T val) 
     { 
      return new U() { Value = val}; 
     } 

     public static implicit operator T(CoolClass<T, U> obj) 
     { 
      return obj.Value; 
     } 

    } 

    public class CoolInt : CoolClass<int, CoolInt> 
    { 
     public CoolInt() 
     { 
     } 

     public CoolInt(int val) 
      : base(val) 
     { 
     } 

     public override int DoSomethingCool() 
     { 
      return this.Value * this.Value; // Ok, so that wasn't THAT cool 
     } 
    } 
0

Sería útil si, al menos para las interfaces, se pudiera declarar que una clase implementa una interfaz en términos de un objeto; esto sería especialmente bueno si hubiera una restricción de tipo genérico "interfaz". Entonces uno podría, por ejemplo, hacer algo como (sintaxis VB)

 
Class Foo(Of T as Interface) 
    Implements T via Bar ' Declares variable 'bar' of type T 
    Sub DoSomething 
    ' Does something 
    End Sub 
End Class 

y luego emitir un Foo (de T) a una T y hacer que se comporte como un T. Tal vez alguien de la EM puede tropezar en la idea y pasarlo?

Debo señalar, por cierto, un patrón similar agradable a su ICoolInterface:

 
public interface ISelf<T> 
{ 
    T Self { get;} 
} 
public interface IFoozle 
{ 
    ... definitions for IFoozle 
} 
public interface IFoozle<T> : ISelf<T> , IFoozle; 
{ 
    /* Empty except for above declarations */ 
} 
... similarly define IWoozle and IWoozle<T> etc. 

Entonces uno puede declarar un campo que puede poner en práctica IWoozle y IFoozle, y hereda de BoozleBase (que implementa ni), a través de:

 
    IWoozle<IFoozle<BoozleBase>> myField1; 
or 
    IFoozle<IWoozle<BoozleBase>> myField2; 

Nota que los dos tipos anteriores se pueden moldear a la otra, o a los tipos que contienen también otras interfaces. Si solo se necesita pasar una variable que cumpla con múltiples restricciones a un método, se puede obtener tal cosa más fácilmente utilizando un método genérico. Desafortunadamente, no hay forma de almacenar un objeto de tipo desconocido en un campo de tal manera que se pueda pasar a una función genérica con múltiples restricciones.

Cuestiones relacionadas