Pregunta

Estoy usando una jerarquía de clases de colección genéricos que se derivan de una clase base abstracta para guardar objetos de entidad que también se derivan de una clase base abstracta:

abstract class ItemBase { }

class MyItem : ItemBase
{
    public MyItem()
    {
    }
}


abstract class CollectionBase<T> : Collection<T> where T : ItemBase, new() { }

class MyCollection : CollectionBase<MyItem> { }

Los objetivos de este modelo son para imponer una estricta disciplina de tipo de los miembros de cualquier clase derivada de CollectionBase y garantizar que estos miembros tienen un constructor público predeterminado. Hasta ahora funciona.

Ahora quiero crear un método de fábrica que devuelve una instancia de una clase derivada de CollectionBase . Yo entiendo que el enfoque habitual sería:

public CollectionBase<T> CreateCollection<T>();

... o tal vez ...

public T Create CreateCollection<T>();

Sin embargo, el problema es que la rutina de llamada no sabe lo que se requiere de "T". Es el método de fábrica en sí que debe determinar el tipo específico de recogida para volver. Así que necesito un método de firma no genérico que dice "el tipo de retorno se derivará de CollectionBase ". Preveo algo como esto (si fuera legal) ...

public CollectionBase<> CreateCollection();

Asumo que esto es otro de los temas de la varianza genéricos espinosos, pero incluso después de leer una amplia explicación de Eric Lippert sobre el tema, estoy todavía no está claro si lo que estoy tratando de hacer es probable que sea factible en C # 4.0, y si hay es una solución simple en C # 3.0.

Gracias de antemano por sus ideas.

¿Fue útil?

Solución

Digamos que el método de fábrica estaban creando un solo elemento. Usted tendría que:

public ItemBase CreateItem();

Usted tiene que usar ItemBase porque no se puede saber nada más específico. La aplicación de este principio al método de recogida, que se obtiene:

public CollectionBase<ItemBase> CreateCollection();

Debido a la falta de varianza, se necesita un adaptador de clase que se deriva de CollectionBase<ItemBase> y envuelve la colección real que se crea.

En C # 4.0 que acaba de regresar de la colección real.

Editar La fábrica podría tener este aspecto, con el adaptador incrustado:

public class CollectionFactory
{
    public CollectionBase<ItemBase> CreateCollection()
    {
        if(someCondition)
        {
            return CreateAdapter(new MyCollection());
        }
        else
        {
            return CreateAdapter(new MyOtherCollection());
        }
    }

    private static CollectionAdapter<T> CreateAdapter<T>(CollectionBase<T> collection) where T : ItemBase, new()
    {
        return new CollectionAdapter<T>(collection);
    }

    private class CollectionAdapter<T> : CollectionBase<ItemBase> where T : ItemBase, new()
    {
        private CollectionBase<T> _collection;

        internal CollectionAdapter(CollectionBase<T> collection)
        {
            _collection = collection;
        }

        // Implement CollectionBase API by passing through to _collection
    }
}

Otros consejos

Es probable que lo que necesita es:

public CollectionBase<Object> CreateCollection();

Debido a que cada clase hereda directa o indirectamente de la clase Object.

No he hecho realmente esto, y disculpas si es completamente ingenuo, pero ...

public CollectionBase<Object> CreateCollection();

Si realmente quiere que sea nada, sólo podría volver ICollection, y luego sus consumidores podría llamar el método de extensión .Cast<ItemBase>() ya que necesitan.

Por lo que entiendo, en .NET 4.0 que sería capaz de volver CollectionBase<ItemBase>, pero no he probado en la versión beta todavía.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top