2008-08-25 7 views
10

¿Hay alguna manera?¿Cómo puede requerir un constructor sin parámetros para los tipos que implementan una interfaz?

Necesito todos los tipos que implementan una interfaz específica para tener un constructor sin parámetros, ¿se puede hacer?

Estoy desarrollando el código base para que otros desarrolladores de mi empresa lo utilicen en un proyecto específico.

Hay un proceso que creará instancias de tipos (en diferentes subprocesos) que realizan ciertas tareas, y necesito que esos tipos sigan un contrato específico (ergo, la interfaz).

La interfaz será interno para el montaje

Si tiene una sugerencia para este escenario sin interfases, con mucho gusto voy tenerlo en cuenta ...

Respuesta

5

Juan Manuel said:

que es una de las razones que no entiendo por qué no puede ser una parte del contrato en la interfaz

Es un mecanismo indirecto. El genérico le permite "engañar" y enviar información de tipo junto con la interfaz. Lo más importante que hay que recordar aquí es que la restricción no está en la interfaz con la que estás trabajando directamente. No es una restricción en la interfaz en sí, sino en algún otro tipo que "circule" en la interfaz. Esta es la mejor explicación que puedo ofrecer, me temo.

Como ilustración de este hecho, señalaré un agujero que he notado en el código de aku. Es posible escribir una clase que compilarse bien pero fallan en tiempo de ejecución cuando intenta crear una instancia:

public class Something : ITest<String> 
{ 
    private Something() { } 
} 

Algo deriva de ITest <T>, pero no se implementa constructor sin parámetros. Se compilará bien, porque String implementa un constructor sin parámetros. De nuevo, la restricción está en T y, por lo tanto, en String, en lugar de en ITest o Something. Como se cumple la restricción sobre T, se compilará. Pero fallará en tiempo de ejecución.

Para evitar algunos instancias de este problema, es necesario agregar otra restricción a T, de la siguiente manera:

public interface ITest<T> 
    where T : ITest<T>, new() 
{ 
} 

Nota la nueva restricción: T: T ITest < >.Esta restricción especifica que lo que pase al parámetro argumento de ITest <T> debe también derivar de ITest <T>.

Aún así esto no impedirá todos los casos de el agujero. El siguiente código compilará bien, porque A tiene un constructor sin parámetros. Pero dado que el constructor sin parámetros de B es privado, la instancia de B con su proceso fallará en el tiempo de ejecución.

public class A : ITest<A> 
{ 
} 

public class B : ITest<A> 
{ 
    private B() { } 
} 
0

Yo no lo creo.

Tampoco puede usar una clase abstracta para esto.

21

No debe ser demasiado contundente, pero ha entendido mal el propósito de las interfaces.

Una interfaz significa que varias personas pueden implementarlo en sus propias clases, y luego pasar instancias de esas clases a otras clases para ser utilizadas. La creación crea un acoplamiento fuerte innecesario.

Parece que realmente necesita algún tipo de sistema de registro, ya sea para que las personas registren instancias de clases utilizables que implementan la interfaz, o de fábricas que pueden crear dichos elementos a pedido.

5

Juan,

Desafortunadamente no hay forma de evitar esto en un lenguaje fuertemente tipado. No podrá garantizar en tiempo de compilación que las clases podrán ser instanciadas por su código basado en Activador.

(ed: eliminado una solución alternativa errónea)

La razón es que, por desgracia, no es posible utilizar interfaces, clases abstractas, o métodos virtuales en combinación con cualquiera de constructores o métodos estáticos. La razón breve es que los primeros no contienen información de tipo explícita, y los segundos requieren información de tipo explícita.

Constructores y métodos estáticos tienen tienen información explícita (justo en el código) tipo disponible en el momento de la llamada. Esto es necesario porque no hay ninguna instancia de la clase involucrada que pueda ser consultada por el tiempo de ejecución para obtener el tipo subyacente, que el tiempo de ejecución necesita para determinar qué método concreto real llamar.

El punto de una interfaz, la clase abstracta y método virtual entera es ser capaz de hacer una llamada a la función sin información de tipo explícito, y esto es posible gracias al hecho de que hay una instancia que se hace referencia, que tiene La información del tipo "oculto" no está disponible directamente para el código de llamada. Entonces estos dos mecanismos son simplemente mutuamente exclusivos. No se pueden usar juntos porque cuando los mezclas, terminas sin ningún tipo de información concreta en ninguna parte, lo que significa que el tiempo de ejecución no tiene idea de dónde encontrar la función a la que le pides que llame.

0

me gustaría recordar a todos que:

  1. Escritura atribuye en .NET es fácil
  2. escritura herramientas de análisis estático en .NET que aseguren la conformidad con las normas de la empresa es fácil

escritura una herramienta para tomar todas las clases concretas que implementan una determinada interfaz/tienen un atributo y verificar que tiene un constructor sin parámetros toma alrededor de 5 minutos de esfuerzo de codificación. Lo agrega a su paso posterior a la compilación y ahora tiene un marco para cualquier otro análisis estático que necesite realizar.

El lenguaje, el compilador, el IDE, su cerebro: todas son herramientas. ¡Usalos, usalos a ellos!

0

No, no puedes hacer eso. ¿Tal vez para su situación una interfaz de fábrica sería útil? Algo como:

interface FooFactory { 
    Foo createInstance(); 
} 

Para cada implementación de Foo crea una instancia de FooFactory que sepa cómo crearla.

0

No necesita un constructor sin parámetros para que el Activador ejemplifique su clase. Puede tener un constructor parametrizado y pasar todos los parámetros desde el Activador. Consulte MSDN on this.

1

Llamar a un método RegisterType con el tipo, y restringirlo utilizando genéricos. Luego, en lugar de caminar ensamblajes para encontrar implementadores de ITest, simplemente guárdelos y cree desde allí.

void RegisterType<T>() where T:ITest, new() { 
} 
4

por lo que necesita una cosa que pueden crear instancias de un tipo desconocido que implementa una interfaz. Básicamente tiene tres opciones: un objeto de fábrica, un objeto Type o un delegado. Aquí es lo dado:

public interface IInterface 
{ 
    void DoSomething(); 
} 

public class Foo : IInterface 
{ 
    public void DoSomething() { /* whatever */ } 
} 

Uso de texto es bastante feo, pero tiene sentido en algunos escenarios:

public IInterface CreateUsingType(Type thingThatCreates) 
{ 
    ConstructorInfo constructor = thingThatCreates.GetConstructor(Type.EmptyTypes); 
    return (IInterface)constructor.Invoke(new object[0]); 
} 

public void Test() 
{ 
    IInterface thing = CreateUsingType(typeof(Foo)); 
} 

El mayor problema con él, es que en tiempo de compilación, usted no tiene ninguna garantía de que Foo realidad tiene un constructor predeterminado. Además, la reflexión es un poco lenta si se trata de un código de rendimiento crítico.

La solución más común es el uso de una fábrica:

public interface IFactory 
{ 
    IInterface Create(); 
} 

public class Factory<T> where T : IInterface, new() 
{ 
    public IInterface Create() { return new T(); } 
} 

public IInterface CreateUsingFactory(IFactory factory) 
{ 
    return factory.Create(); 
} 

public void Test() 
{ 
    IInterface thing = CreateUsingFactory(new Factory<Foo>()); 
} 

En lo anterior, IFactory es lo que realmente importa. Factory es solo una clase de conveniencia para las clases que do proporcionan un constructor predeterminado. Esta es la solución más simple y a menudo mejor.

El tercer momento-raro-pero-probable-a-convertido-más-común solución es el uso de un delegado:

public IInterface CreateUsingDelegate(Func<IInterface> createCallback) 
{ 
    return createCallback(); 
} 

public void Test() 
{ 
    IInterface thing = CreateUsingDelegate(() => new Foo()); 
} 

La ventaja aquí es que el código es corto y simple, puede trabajar con cualquier método de construcción y (con cierres) le permite transferir fácilmente los datos adicionales necesarios para construir los objetos.

Cuestiones relacionadas