2010-06-15 16 views
8

Tengo una situación en la que tengo una claseinterfaz para el método que devuelve su propio tipo

class Foo 
{ 
    Foo Bar() 
    { 
     return new Foo(); 
    } 
} 

Ahora i wan tot crear una interfaz para que

class IFoo 
{ 
    ??? Bar(); 
} 

¿Cuál debería ser en lugar de la ¿signos de interrogación? Cada clase debe devolver su propio tipo, no Foo.

Las siguientes soluciones funcionan pero no se ven limpias. No entiendo por qué tengo que especificar la misma clase dos veces, y no hay nada como "presente" para el tipo actual

Así es como lo estoy usando más tarde

class GenericClass<T> where T : IFoo 
{ 
    T foo = new T(); 
    T item = foo.Bar(); 
} 
+1

El problema con una interfaz genérica es que ahora necesita especificar T en cualquier lugar que utilice esta interfaz en el código, de modo que ha acoplado su interfaz al tipo subyacente. Las interfaces tienden a intentar hacer lo opuesto? –

+0

@Andrey - Como dijo Adam, este es un olor de código serio. Mi respuesta hará lo que está buscando, pero no es una buena práctica. – GenericTypeTea

+0

@GenericTypeTea, lo siento, debería haber aclarado que funcionaría :) –

Respuesta

9

le preguntas:

La soluciones bel ow trabajo, pero no se ve limpio. No entiendo por qué tengo que especificar la misma clase dos veces, y no hay nada como "esto" para el tipo actual

La razón por la que tiene que especificarlo dos veces es porque C# carece de la función que usted necesitar. Lo que queremos es algo como esto:

interface IFoo 
{ 
    IFoo Bar(); 
} 

class Foo : IFoo 
{ 
    Foo Bar() // should work since Foo is an IFoo, but it's not supported by C# 
    { 
     return new Foo(); 
    } 
} 

Desde el punto de vista de seguridad de tipos, esto debería funcionar (se llama return type covariance). De hecho, otros lenguajes de programación como C++ o Java lo admiten, vea this example on Wikipedia. Desafortunadamente, , la covarianza de tipo de retorno no es compatible con C# (ni siquiera con C# 4.0, que introdujo la covarianza para genéricos), por lo que debe usar la "solución genérica" ​​ilustrada en las otras respuestas.

tipos de retorno covariantes, así como "este" tipo son características propuestas para las nuevas versiones de C#:

+0

Su código no se compilará porque la clase Foo no implementa 'IFoo Bar()'. Puede modificar Foo esto para hacer que Bar devuelva un IFoo, pero significará que necesita subir el IFoo a Foo desde cualquier lugar en el que desee acceder a los métodos de Foo. (aunque seguro de hacer, no muy ordenado para escribir).
Covarianza y contravarianza son compatibles con tipos genéricos a partir de C# 4.0, pero en realidad no son necesarios para este escenario. La solución GenerticTypeTea publicada es suficiente. –

+0

@Mark H - Creo que lo que Heinzi está diciendo es "esto es lo que quiere el OP ... pero no es posible". – GenericTypeTea

+0

@GenericTypeTea: Exactamente. :-) En realidad, estoy diciendo "No es posible en C# (pero en otros idiomas)". – Heinzi

8

Se podría añadir una tipo genérico y uso de la restringen el tipo de interfaz:

public interface IFoo<T> 
{ 
    T Bar(); 
} 

te había implementen esto de la siguiente manera:

public class Foo : IFoo<Foo> 
{ 
    public Foo Bar() 
    { 
     return new Foo(); 
    } 
} 

public class Cheese : IFoo<Cheese> 
{ 
    public Cheese Bar() 
    { 
     return new Cheese(); 
    } 
} 

actualización, si nunca se preocupan por el tipo de retorno concreto de Foo, entonces usted puede hacer lo siguiente:

public interface IFoo 
{ 
    IFoo Bar(); 
} 

que se implementa como:

public class Foo : IFoo 
{ 
    public IFoo Bar() 
    { 
     return new Foo(); 
    } 
} 

Luego, en su clase genérica:

public class GenericClass<T> where T : class, IFoo, new() 
{ 
    public T Rar() 
    { 
     T foo = new T(); 
     T item = foo.Bar() as T; 
     return item; 
    } 
} 

GenericClass<Foo>.Rar(); será una implementación concreta de Foo.

+0

Esto no se compilará. – SLaks

+0

@SLaks: solo necesita agregar 'public', ¿no? –

+0

me parece bien. ¿Por qué no debería compilarse? – jalf

0
public interface IFoo<T> 
{ 
    T Bar(); 
} 

Su aplicación sería entonces:

class Foo : IFoo<Foo> 
{ 
    Foo Bar() 
    { 
     return new Foo(); 
    } 
} 

class Baz : IFoo<Baz> 
{ 
    Baz Bar() 
    { 
     return new Baz(); 
    } 
} 
0

Usted necesita hacer la interfaz genérica, como esto:

interface IFoo<TClass> where TClass : IFoo<TClass>, class { 
    TClass Bar(); 
} 
2

Puede utilizar una clase base abstracta, más la implementación miembro explícito a lograr esto. En primer lugar, declarar la interfaz de la siguiente manera:

interface IFoo 
{ 
    IFoo Bar(); 
} 

A continuación, declarar una clase abstracta genérico que implementa IFoo de una manera explícita, y también declara un método abstracto ese tipo de "sobrecarga" Bar(), pero en un genérico forma:

abstract class BaseFooImpl<T> : IFoo where T : BaseFooImpl 
{ 
    public abstract T Bar(); 

    IFoo IFoo.Bar() 
    { 
     return Bar(); // this will call the abstract Bar() 
    } 
} 

Ahora, definir sus clases concretas de esta manera:

class ConcreteFoo : BaseFooImpl<ConcreteFoo> 
{ 
    public override ConcreteFoo Bar() 
    { 
     return this; // for example, of course. 
    } 
} 

La ventaja de este enfoque es que siempre se puede utilizar referencias IFOO no genéricas para contener instancias concretas.Si usted hace su interfaz genérica, no se puede, por ejemplo, declarar siguientes:

IFoo mammalInstance, fishInstance; // Instead of IFoo<Mammal> mammalInstance; IFoo<Fish> fishInstance; 
List<IFoo> manyInstances; // Instead of List<IFoo<IFoo>>, which doesn't even work AFAIK 
+0

¿Y qué impide que alguien diga la clase C2: BaseFooImpl ? Nada. Esto no obliga a Bar a devolver C2. –

+0

@Eric, eso es inquietante. Incluso invalida mi respuesta, si nos atenemos a las necesidades del OP. Supongo que este problema es común a las otras soluciones publicadas aquí también. Sin embargo, 'C2' (o mejor, su autor) sabe lo que está haciendo. – Humberto

+0

@Eric ¿Hay alguna forma de restringir algo para que devuelva su propio tipo utilizando una interfaz? Incluso "ICloneable" solo obliga al codificador a devolver 'object'. Podría fácilmente hacer que 'Apple' devolviera' Orange' – DevinB

0

No está seguro de lo que está tratando de lograr, pero se podría hacer de esta manera:

interface IFoo<T> 
{ 
    T Bar(); 
} 



    class Foo:IFoo<Foo> 
    { 

     #region IFoo<Foo> Members 

     public Foo Bar() 
     { 
      return new Foo(); 
     } 

     #endregion 
    } 

O igual esto:

interface IFoo 
    { 
     IFoo Bar(); 
    } 

class Foo : IFoo 
    { 

     #region IFoo Members 

     public IFoo Bar() 
     { 
      return new Foo(); 
     } 

     #endregion 
    } 
4

creo que la verdadera pregunta es: ¿por qué necesitas el tipo derivado en la interfaz? La interfaz es exactamente por esa razón: abstracción de las clases concretas. Si es sólo por conveniencia, por lo que no tiene que echar a Foo después de llamar a Bar(), se puede implementar la interfaz de forma explícita:

interface IFoo 
{ 
    IFoo Bar(); 
} 

class Foo : IFoo 
{ 
    public Foo Bar() 
    { 
     return new Foo(); 
    } 

    IFoo IFoo.Bar() 
    { 
     return Bar(); 
    } 
} 

hacerse la pregunta: ¿por qué se introduce una interfaz cuando se desea el tipo concreto?

+0

+1 Esto es exactamente lo que estaba pensando. – juharr

+0

Para dar un ejemplo simple: Este objeto se usa luego para enlazar una grilla. En ese momento, no uso la interfaz que solo cubre la basura, sino que dependo de las propiedades que representan las columnas – Andrey

Cuestiones relacionadas