2013-04-18 15 views
15

Se ho una classe Item generica che assomiglia a questo:C# generici: Semplificare tipo di firma

abstract class Item<T> 
{ 
} 

ed un contenitore di elementi che assomiglia a questo:

class Container<TItem, T> 
    where TItem : Item<T> 
{ 
} 

Dal TItem dipende da T , è possibile semplificare la firma del tipo di Container in modo che richieda un solo parametro di tipo? Quello che voglio davvero è qualcosa di questo:

class Container<TItem> 
    where TItem : Item // this doesn't actually work, because Item takes a type parameter 
{ 
} 

così posso istanziare come segue:

class StringItem : Item<string> 
{ 
} 

var good = new Container<StringItem>(); 
var bad = new Container<StringItem, string>(); 

Il compilatore dovrebbe essere in grado di dedurre che T è stringa quando TItem è StringItem, giusto? Come faccio a far succedere questo?

utilizzo desiderata:

class MyItem : Item<string> 
{ 
} 

Container<MyItem> container = GetContainer(); 
MyItem item = container.GetItem(0); 
item.MyMethod(); 
+0

penso che si può. Il suo polimorfismo. Puoi sovrascrivere i metodi astratti. –

+0

In questo caso è possibile creare classe StringContainer come classe StringContainer dove TItem: Oggetto RockWorld

+0

non credo che il compilatore può dedurre _partially_ tipi generici. –

risposta

2

Questo dovrebbe fare quello che vuoi che penso. Ovviamente ora stai facendo Container<string> non Container<StringItem> ma dato che non hai incluso esempi di utilizzo, non riesco a vederlo come un problema.

using System.Collections.Generic; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var myContainer = new Container<string>(); 

      myContainer.MyItems = new List<Item<string>>(); 
     } 
    } 

    public class Item<T> { } 

    public class Container<T> 
    { 
     // Just some property on your container to show you can use Item<T> 
     public List<Item<T>> MyItems { get; set; } 
    } 
} 

Come su questa versione riveduta:

using System.Collections.Generic; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var myContainer = new Container<StringItem>(); 

      myContainer.StronglyTypedItem = new StringItem(); 
     } 
    } 

    public class Item<T> { } 

    public class StringItem : Item<string> { } 

    // Probably a way to hide this, but can't figure it out now 
    // (needs to be public because it's a base type) 
    // Probably involves making a container (or 3rd class??) 
    // wrap a private container, not inherit it 
    public class PrivateContainer<TItem, T> where TItem : Item<T> { } 

    // Public interface 
    public class Container<T> : PrivateContainer<Item<T>, T> 
    { 
     // Just some property on your container to show you can use Item<T> 
     public T StronglyTypedItem { get; set; } 
    } 
} 
+0

Questo compila, ma ho davvero bisogno del contenitore , non del contenitore . – brianberns

+2

@brianberns: perché? (Non lamentarsi, sarebbe utile solo per le potenziali risposte) –

+0

L'utilizzo diventa complesso, ma immagina che ci sia un metodo sul contenitore chiamato GetItem (int index) che deve restituire un TItem, non un articolo . Ciò consente al chiamante di invocare i metodi TItem direttamente sul risultato, senza doverlo trasmettere dall'elemento a TItem. – brianberns

1

Penso che una possibile soluzione al vostro problema è l'aggiunta di un'interfaccia IItem e la struttura del codice sarà come segue.

interface IItem { } 

abstract class Item<T> : IItem { } 

class Container<TItem> where TItem : IItem { } 

class StringItem: Item<string> { } 

E ora si può avere la Container<StringItem>:

var container = new Container<StringItem>(); 
+0

Un buon suggerimento, ma il problema con questo è che Container ha davvero bisogno di sapere sul parametro T sottostante di TItem. – brianberns

+0

Quindi non è possibile. – hazzik