Generics sind im Allgemeinen der richtige Weg. Die gesteigerte Komplexität könnte ein Argument dagegen sein, aber meiner Meinung nach lohnt es sich absolut, die Vorteile in den meisten Fällen in Betracht zu ziehen. Als Faustregel, umarme es. Reflection ist ein weiterer Fall, bei dem Generics es schwieriger machen. Aber ich vermeide Generics nur, wenn es keinen Sinn ergibt.
Das eine Szenario, in dem ich Generics vermeide, ist, wenn eine Methode generisch erstellt wird, wenn sie keinen beabsichtigten Zweck von Generics erfüllt und irreführend sein kann. Generics wurden für eine bessere Typsicherheit und Performance (bei Werttypen) eingeführt. Wenn es weder das eine noch das andere erfüllt, kann ein generischer Ansatz den Entwickler in die Irre führen. Zum Beispiel betrachten Sie den Ausschnitt aus dieser Frage:
public void DoSomething(T request)
{
if (request == null)
throw new ArgumentNullException("request");
if (request is ISomeInterface)
DoSomething();
else
DoSomethingElse();
}
In der obigen Methode wird nichts generisch behandelt. Es wird noch schlimmer, wenn Sie in einer generischen Methode viele Typüberprüfungen durchführen:
public void DoSomething(T request)
{
if (typeof(T) == typeof(X))
DoSomething();
else if (typeof(T) == typeof(Y))
DoSomethingElse();
...
}
was dem Sinn von Generics komplett widerspricht. Es gibt bessere Muster, um Typüberprüfungen zu vermeiden, aber wenn es unbedingt erforderlich ist, dann bevorzuge ich die Annahme von object
als Parameter. Ein weiterer ähnlicher Fall ist, wenn Werttypen sowieso in einer generischen Methode geboxt werden:
public void DoSomething(T request)
{
var type = request.GetType(); //schon geboxt im Fall von Werttypen.
//oder andere Reflektionsaufrufe, die eine Boxung beinhalten
}
Anhand der Methodendefinition ist man versucht zu denken, dass diese Methode keine Boxungsstrafen hat, aber es dem Aufrufer deutlich zu machen, dass es eine gute Möglichkeit ist. Ich mag es so:
public void DoSomething(object request) //boxt hier
{
var type = request.GetType();
//oder andere Reflektionsaufrufe, die eine Boxung beinhalten
}
Abgesehen davon, bevorzuge ich Generics die ganze Zeit.
Ein Randfall, in dem sich die beiden unterschiedlich verhalten könnten, ist bei Verwendung von dynamic
.
public void Generic(T request)
{
}
public void Object(object request)
{
}
dynamic d = null;
Object(d); //keine Probleme
Generic(d); //Laufzeitexplosion; Typargument kann nicht abgeleitet werden
Nicht von Bedeutung, nur eine alberne Verhaltensdifferenz, die es wert ist, im Hinterkopf zu behalten.
0 Stimmen
Neu bearbeiten: "Ein nicht generisches Ding ermöglicht es, den Parameter typ zur Laufzeit zu definieren." - nein, es gibt keinen Parameter zu definieren. Wenn überhaupt, ist dieser Satz rückwärts; ein nicht-generisches aber getyptes Argument beschränkt dich auf einen Typ, während ein generisches Argument dem Aufrufer die Auswahl lässt - also bekommen Generics den Vorteil. Ansonsten bist du hauptsächlich auf
object
beschränkt.0 Stimmen
Bezüglich Ihres Punktes zum Team gegen Einzelperson: Ich sehe einfach keinen Grund, der irgendetwas anders macht; es sei denn, Sie würden absichtlich einen Weg gehen, um Ihre Methoden weniger klar zu machen? Wenn ja, könnten Sie sie alle auch umbenennen in
void A(object a, object b, object c)
. Der bessere Weg, es zu betrachten, ist einfach: gilt diese Methode für einen spezifischen Typ, oder kann die gleiche Logik für einen beliebigen Typ verwendet werden; wenn letzteres, sind Generika eine gute Wahl.