2011-06-05 5 views
7

Si declaro una interfaz en C#, ¿hay alguna manera de que pueda declarar explícitamente que cualquier tipo que implemente esa interfaz es un tipo de referencia?Especifique que una interfaz solo puede implementarse mediante los tipos de referencia C#

La razón por la que quiero hacer esto es para que donde sea que use la interfaz como parámetro de tipo, no tenga que especificar que el tipo de implementación también tenga que ser un tipo de referencia.

ejemplo de lo que quiero lograr:

public interface IInterface 
{ 
    void A(); 
    int B { get; } 
} 

public class UsingType<T> where T : IInterface 
{ 
    public void DoSomething(T input) 
    { 
     SomeClass.AnotherRoutine(input); 
    } 
} 

public class SomeClass 
{ 
    public static void AnotherRoutine<T>(T input) 
     where T : class 
    { 
     // Do whatever... 
    } 
} 

Como no se requiere el argumento a SomeClass.AnotherRoutine() ser un tipo de referencia, aquí voy a conseguir un error de compilación en la que llamo el método, lo que sugiere que fuerzo T ser un tipo de referencia (where T : IInterface, class en la declaración de UsingType). ¿Hay alguna manera de que pueda aplicar esto ya en el nivel de interfaz?

public interface IInterface : class 

no funciona (obviamente) pero tal vez hay otra manera de lograr lo mismo?

+0

Parece que el tipo sería un detalle de implementación, que es lo que la interfaz intenta abstraer en primer lugar. Soy curioso; ¿Por qué solo quieres permitir implementaciones de tipo de referencia? –

+0

No creo que haya una manera de hacerlo, pero ¿realmente es mucho más trabajo agregar una cláusula adicional 'where' en su clase' UsingType <> '? – Cameron

+0

@Ed: En mi caso específico, tengo una interfaz 'IEntity' que dejo que implementen todas mis entidades comerciales. Entity Framework exige que todos los tipos de entidad sean tipos de referencia, por lo que para todas las interacciones con EF esto es obligatorio. Como sé que no quiero tipos de entidad que no sean tipos de referencia, me gustaría especificar eso ya en la interfaz. –

Respuesta

0

No creo que pueda restringir las interfaces de esa manera desafortunadamente. De acuerdo con msdn, las interfaces pueden implementarse por cualquier tipo, tanto estructuras como clases:/

+0

Interfaces generales, sí, pero ¿hay alguna forma de aplicar una restricción de tipo en la interfaz? –

+0

No estoy seguro de entender la distinción, pero que yo sepa, no es posible poner ese tipo de modificaciones en una interfaz. Sin embargo, ¿puede profundizar en los problemas que está viendo con restricciones de tipo? Esa parece ser la mejor solución para mí :) – aL3891

3

Si está pasando algo por debajo de una interfaz, incluso si tiene un tipo de valor implementando esa interfaz, se convertirá en un recuadro si se envía al interactuar y comportarse como un tipo de referencia (porque está encuadrado dentro de un tipo de referencia).

interface IFoo { 
    int Value { get; set; } 
} 

struct Foo : IFoo { 
    public int Value { get; set; } 
} 

observar los efectos cuando se usa como un tipo de valor:

var a = new Foo() { Value = 3 }; 
var b = a; // copies value 
b.Value = 4; 
Console.WriteLine("a has {0}", a.Value); //output: a has 3 
Console.WriteLine("b has {0}", b.Value); //output: b has 4 

Ahora mira lo que pasa cuando lanzas a la interfaz:

var a = new Foo() { Value = 3 } as IFoo; //boxed 
var b = a; // copies reference 
b.Value = 4; 
Console.WriteLine("a has {0}", a.Value); //output: a has 4 
Console.WriteLine("b has {0}", b.Value); //output: b has 4 

Por lo tanto, no importa si una estructura o clase implementa la interfaz. Si se lanza a la interfaz y luego se pasa por debajo de la interfaz, se comportará como un tipo de referencia.

Editar: Entonces, si estos son sus requisitos ...

Por contrato X:

  1. Lanzar un error de compilación si se implementa una estructura/hereda X.
  2. X puede no ser una clase abstracta

Bueno, simplemente estás atascado porque se contradicen entre sí.

  • La única forma de obtener un error de compilación si la estructura implementa/hereda el contrato es si se trata de una clase abstracta.
  • Dado que no puede usar una clase abstracta para mantener abiertas las opciones de herencia, debe usar una interfaz.
  • Las únicas formas de aplicar la regla de que una estructura no puede implementar la interfaz serán durante el tiempo de ejecución.

El uso de la restricción where T: class, IFoo ni siquiera funcionaba todo el tiempo. Si tuviera este método (basado en el mismo Foo y IFoo arriba):

static void DoSomething<T>(T foo) where T: class, IFoo { 
    foo.Value += 1; 
    Console.WriteLine("foo has {0}", foo.Value); 
} 

entonces sería lanzar un error de compilación en esta circunstancia:

var a = new Foo(){ Value = 3 }; 
DoSomething(a); 

pero funcionaría muy bien en esta circunstancia :

var a = new Foo(){ Value = 3} as IFoo; //boxed 
DoSomething(a); 

en lo que a mí respecta, utilice where T: class, IFoo limitación de estilo, y luego puede que no importe si un implem struct en la interfaz siempre que esté encuadrada. Depende de lo que comprueba EF si pasa una estructura en caja, sin embargo. Tal vez funcionará.

Si esto no funciona, al menos la restricción genérica le consigue vías parte allí, y puedes leer la foo.GetType().IsValueType (en referencia a mi método DoSomething arriba) y lanzar una ArgumentException para manejar el caso de estructuras en caja.

+0

Sé que hace la diferencia, y lo que quiero hacer aquí es forzar el tipo de implementación a ser un tipo de referencia, es decir, hacer que su primer ejemplo de código falle la compilación (dándome un error que 'Foo' debe ser un tipo de referencia, o algo así). En otras palabras, mi preocupación no es "¿qué sucede si una estructura implementa mi interfaz?", Sino más bien "¿cómo me aseguro de que una estructura nunca lo haga?". –

0

El comportamiento del box de las estructuras mutables emitidas como interfaces ciertamente puede ser molesto. Sin embargo, no sé si la prohibición de todas las estructuras sería necesaria. Hay una serie de casos en los que puede ser útil tener una estructura inmutable envolviendo un objeto de clase (por ejemplo, una forma de implementar algo así como un diccionario que admite múltiples formas de enumeración habría sido tener Dictionary.Keys y Dictionary.Values ​​ambos estructuras, cada una contiene una referencia a un Diccionario, y cada una proporciona un método GetEnumerator especial; Dictionary no se implementa de esa manera, pero no es un mal patrón). Tal patrón puede evitar el boxeo en los casos en que uno llama a GetEnumerator directamente sobre el objeto (como lo hacen tanto vb.net como C# con sus bucles foreach basados ​​en patos); porque las estructuras son inmutables, incluso cuando el boxeo ocurre, es un problema de rendimiento en lugar de uno de corrección.

Cuestiones relacionadas