2009-05-12 8 views
17

Mam ogólną klasę opakowania, która ma być używana z zestawem typów. Te typy są generowane przez narzędzie i wszystkie pochodzą z klasy bazowej ClientBase. Podczas gdy ClientBase ma tylko domyślny konstruktor, wszystkie wygenerowane typy mają domyślny konstruktor, a konstruktor bierze ciąg jako parametr. W konstruktorze klasy opakowania generuję instancję typu za pomocą konstruktora pobierającego ciąg znaków. Oto przykładowy kod:Jak ograniczyć typ ogólny do musi mieć construtor, który ma pewne parametry?

public class ClientBase 
{ } 

public class GenericProxy<T> 
    where T: ClientBase, new() 
{ 
    T _proxy; 

    public GenericProxy(string configName) 
    { 
     _proxy = new T(configName);  
    } 
} 

Ten kod nie kompiluje ponieważ typ T nie jest gwarantowana mieć konstruktor, który pobiera ciąg. Czy istnieje sposób zdefiniowania ograniczenia dla klasy generycznej, aby wymusić, że typ T musi mieć konstruktor pobierający ciąg znaków? Jeśli nie jest to możliwe, jakie są dobre alternatywy dla radzenia sobie z taką sytuacją?

Odpowiedz

24

To nie jest możliwe. Chciałbym zobaczyć "static interfaces" sobie z tym poradzić, ale nie należy się spodziewać ich w najbliższym czasie ...

Alternatywy:

  • określić pełnomocnika do działania jako fabryki dla T
  • Określ inny interfejs do działania jako fabryki dla T
  • Określ interfejs na samej T dla inicjalizacji (i dodać ograniczenie tak, że T implementuje interfejs)

Th Pierwsze dwa są naprawdę ekwiwalentne. Zasadniczo chcesz zmienić klasę proxy do czegoś takiego: (. Zakładam, że będzie chciał utworzyć więcej wystąpień później - w przeciwnym razie równie dobrze można przekazać instancję T do konstruktora)

public class GenericProxy<T> 
    where T: ClientBase, new() 
{ 
    string _configName; 
    T _proxy; 
    Func<string, T> _factory; 

    public GenericProxy(Func<string, T> factory, string configName) 
    { 
     _configName = configName; 
     _factory = factory; 
     RefreshProxy(); 
    } 

    void RefreshProxy() // As an example; suppose we need to do this later too 
    { 
     _proxy = _factory(_configName); 
    } 
} 

1

Jak zauważa Jon, nie ma w nim wbudowanej obsługi - ale można odłożyć na bok pisanego delegata do konstruktora (szybciej niż odbicie) przy użyciu Expression. Kod do tego można znaleźć w MiscUtil (w MiscUtil.Linq.Extensions.TypeExt).

1

nie odpowiedzieć na rzeczywiste pytania, ograniczający sposób, ale dla kompletności oto jak można zrobić to pytasz w czasie wykonywania, za pomocą refleksji:

private T Get<T>(string id) 
    { 
    var constructor = typeof(T).GetConstructor(new Type[] { typeof(X), typeof(Y) }); 
    if (constructor == null) throw new InvalidOperationException("The type submitted, " + typeof(T).Name + ", does not support the expected constructor (X, Y)."); 

    var data = GetData(id); 
    return (T)constructor.Invoke(new object[] { data.x, data.y }); 
    } 
0

Tutaj opiera pełny przykład roboczych na @JonSkeet odpowiedź:

using System; 
using System.Collections.Generic; 

namespace GenericProxy 
{ 
    class Program 
    { 
     static void Main() 
     { 
      GenericProxy<ClientBase> proxy = new GenericProxy<ClientBase>(ClientBase.Factory, "cream"); 

      Console.WriteLine(proxy.Proxy.ConfigName); // test to see it working 
     } 
    } 

    public class ClientBase 
    { 
     static public ClientBase Factory(string configName) 
     { 
      return new ClientBase(configName); 
     } 

     // default constructor as required by new() constraint 
     public ClientBase() { } 

     // constructor that takes arguments 
     public ClientBase(string configName) { _configName = configName; } 

     // simple method to demonstrate working example 
     public string ConfigName 
     { 
      get { return "ice " + _configName; } 
     } 

     private string _configName; 
    } 

    public class GenericProxy<T> 
     where T : ClientBase, new() 
    { 
     public GenericProxy(Func<string, T> factory, string configName) 
     { 
      Proxy = factory(configName); 
     } 

     public T Proxy { get; private set; } 
    } 
} 

Spodziewać się następujący wynik: ice cream

Powiązane problemy