2015-12-05 16 views
15

Mam trudno jest zrozumieć, dlaczego korzystne byłoby zrobić coś takiego: (Próbka jest klasa)Jaki jest cel ograniczenia rodzaju generycznego w metodzie?

static void PrintResults<T>(T result) where T : Sample 

nie byłoby lepiej po prostu przekazać próbkę do metody?

static void PrintResults (Sample result) 
+6

W przypadku rzeczy, które są przekazywane do metod korzyści są rzeczywiście niewielkie. W przypadku rzeczy, które są ponownie zwracane, pomaga to nieco więcej, ponieważ konsument nie musi przesyłać. – Joey

Odpowiedz

10

Polecam omijanie typów ogólnych, w których działa nietypowa składnia, na przykład podany przykład. Istnieją jednak inne przydatne przypadki.

Na przykład, określając typ zwracany rodzajowo:

static T Create<T>() where T: Sample, new() 
{ 
    return new T(); 
} 

// Calling code 
Sample sample = Create<Sample>(); 

zamiast

static object Create() 
{ 
    return new Sample(); 
} 

// Calling code 
Sample sample = (Sample) Create(); 

Można również użyć szablonów umieszczenie wielu ograniczeń typu. Na przykład:

static T Create<T>() where T: IMyInterface, new() 
{ 
    return new T(); 
} 

interface IMyInterface {} 
class MyClass : IMyInterface { } 

// Calling code. 
MyClass myClass = Create<MyClass>(); 

Umożliwia to generyczne tworzenie nowego typu, który implementuje określony interfejs i ma ogólny konstruktor. Też:

static void DoSomething<T>(T t) where T: IMyInterface1, IMyInterface2 
{ 
    t.MethodOnIMyInterface1(); 
    t.MethodOnIMyInterface2(); 
} 

interface IMyInterface1 
{ 
    void MethodOnIMyInterface1(); 
}  
interface IMyInterface2 
{ 
    void MethodOnIMyInterface2(); 
}  
class MyClass: IMyInterface1, IMyInterface2 
{ 
    // Method implementations omitted for clarity 
} 

// Calling code 
MyClass myclass' 
DoSomething(myclass); // Note that the compiler infers the type of T. 

Gdzie można wymagać wiele interfejsów za pomocą pojedynczego parametru bez (1) tworząc nowy typ, który implementuje te wszystkie interfejsy i (2) wymaga parametrów do danego typu.

Jak zaznacza @dcastro w swojej/jej odpowiedzi, typy ogólne mogą również informować kompilator, aby wymagał typów są takie same. Na przykład:

static void DoSomething<T>(T t1, T t2) where T: MyType 
{ 
    // ... 
} 

class MyType {} 
class MyType1: MyType {} 
class MyType2: MyType {} 

// Calling code 
MyType1 myType1; 
MyType2 myType2; 
DoSomething<MyType>(myType1, myType2); 

Jeżeli kompilator wymaga t1 i t2 są tego samego typu, ale może być dowolnego typu, który dziedziczy MyType. Jest to przydatne w zautomatyzowanych strukturach testowania jednostkowego, takich jak NUnit lub MSTest, do generycznych sprawdzeń równości i porównywania.

+1

Bit na końcu o zmuszaniu tych samych typów jest nieprawidłowy. Dowolny z nich kompilowałby dobrze "DoSomething (mytype1, mytype2); DoSomething ((MyType) mytype1, mytype2); '... – Brandon

+0

@akton dzięki za szczegółową odpowiedź. Naprawdę doceniłem wszystkie dostarczone odpowiedzi! Zdaję sobie sprawę, że nie dałem najlepszego przykładu do pracy, ale udało ci się dobrze odpowiedzieć na pytanie. Dzięki jeszcze raz! – Anonymous

+0

@Brandon Pytanie nie brzmi, czy rzeczy się skompilują. Typy ogólne (1) ułatwiają czytanie kodu (chociaż nie w przypadku OP) i (2) przenoszą więcej kontroli na czas kompilacji ze środowiska wykonawczego (zwykle przez zastąpienie rzutów). W konkretnym przypadku nie jest to najlepszy przykład, ale jest wystarczająco dobry, aby odpowiedzieć na pytanie PO. – akton

2

W pustych przestrzeniach można zawsze używać interfejsu jako parametru, aby działało wiele typów, więc generics nie są tu często przydatne.

Jedynymi wyjątkami są ograniczenia dotyczące leków generycznych. I przez to nie mam na myśli czegoś takiego jak gdzie T: IA, IB , ponieważ można to zrobić za pomocą interfejsu, który zarówno implementuje IA i IB. W pewnym momencie stanie się to męczące, ponieważ będziesz potrzebował coraz więcej interfejsów. Więc spójrzmy ath klasy „specjalne ograniczenia” i nowy

public void AddNew(List<T> items) where T : new 
{ 
    items.Add(new T()); 
} 

i klasę, która jest użyteczna, jeśli metoda mutuje jego parametr, który będzie działać na elemencie

static void IncA<T>(T item) where T: class, IA 
{ 
    item.A++; 
} 

Prawdziwa siła generycznych jest, gdy metody mają ogólny zwrotny typ lub ogólne klasy, takie jak lista <T>. Nie chcesz zaimplementować nowej klasy dla każdej listy, której będziesz potrzebować.

3

Większość odpowiedzi zawiera wyjaśnienia dotyczące przydatności leków generycznych, w których występują interfejsy, które nie wydają się odpowiadać na twoje aktualne pytanie.

Prawda jest taka, że ​​dla przykładu, który wysłałeś, nie ma żadnej korzyści z zastosowania ogólnej metody. W rzeczywistości jest to gorzej, ponieważ spowoduje to wygenerowanie wielu implementacji tej samej funkcji i zawsze nieznacznie zwiększy rozmiar kodu w czasie wykonywania.

+1

Nie sądzę, aby wspomnieć o "wielu implementacjach" w jego przypadku, ponieważ 'Sample' jest koniecznie typem referencyjnym (w rzeczywistości mówi nam, że jest to' klasa'). W przypadku typów referencyjnych nie ma "wielu implementacji". Poza tym, możesz mieć rację, nie ma wiele korzyści w jego szczególnym scenariuszu. Otrzymujemy dodatkowy typ 'T' (który jest typem czasu kompilacji' typeof (T) 'niekoniecznie jest identyczny z' result.GetType() '). Metoda może używać tego typu, na przykład 'var config = HelperClass .GetConfig();' lub coś podobnego. –