Autor Beitrag
Csharp-programmierer
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Sa 02.04.16 13:07 
Hallo :)

Ich habe mich jetzt informiert, was Generics sind und habe auch schon ein Testprogramm geschrieben. Der Sinn ist ja, dass eine Klasse/Funktion beliebige Datentypen entgegennehmen kann, die frei wählbar sind.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Compare<string> _compare = new Compare<string>();
            MessageBox.Show(_compare.Check("Hallo""Hallo").ToString());

            Compare<int> _compare1 = new Compare<int>();
            MessageBox.Show(_compare1.Check(01).ToString());
        }
    }

    public class Compare<AnyDataType>
    {
        public bool Check(AnyDataType data1, AnyDataType data2)
        {
            return data1.Equals(data2) ? true : false;
        }
    }


Ich verstehe dies zwar, aber wozu ist das gut? Da fehlt mir die Vorstellungskraft, wozu man sowas braucht.

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Sa 02.04.16 14:05 
Vorweg: Es hat sich eingebürgert, dass generiche Typ-Parameter immer mit einem T beginnen. Wie das I bei einem Interface ^^


Wozu Generics da sind:
Du hast doch sicher schon mal mit einer Liste gearbeitet, oder? Die ist auch generich.
Du gibst der den Typ an, mit der sie arbeiten soll und ab diesem Zeitpunkt hast Du überall, wo dieser generische Typ verwendet wird, genau den Typ, den Du angegeben hast. Eine List<string> verlangt in der Add-Methode einen string und auch der Indexer arbeitet mit einem String. Gibst Du Int als Typ an, hast Du überall Int.
Fällt der generische Aspekt weg (wie bei der ArrayList), dann kannst Du zwar auch sagen, in der Liste sollen Strings sein, aber Du kannst genauso gut auch ein Int hinzufügen. Wenn Du dann die Strings in der Liste auswerten willst, bekommst Du einen Fehler.
Die generische Liste hat damit den Vorteil, dass sie mit jedem Typen funktioniert, typisiert bleibt und der Compiler das sogar nicht überprüft.

Zusätzlich hast Du die Möglichkeit, dem generischen Typ-Parameter Einschränkungen zu verpassen. Z.B. kannst Du sagen, dass der Typ ein bestimmtes Interface implementieren soll, oder es soll eine Klasse sein, oder einen parameterlosen Konstruktor haben.
Wenn Du dann diesen generischen Typ-Parameter verwendest, kannst Du auf diese Einschrönkungen aufbauen. So hat der Typ dann alle Member des Interfaces, ein Objekt davon darf null sein (Was z.B. den as-Operator möglich macht) und Du kannst eine Instanz erstellen - und das ohne zu wissen, was Du da eigentlich hast.
Gleichzeitig wird bei der Nutzung der generischen Klasse darauf geachtet, dass der Typ, den Du übergibst, auch den Anforderungen entspricht.

Bei Methoden hast Du die gleichen Vorteile.
Ein sehr gutes Beispiel ist z.B. Linq, denn fast jede Erweiterungsmethode ist generisch.
So kannst Du bei der Where-Methode eine Function übergeben, die die Bedingung für das Where dar stellt. Die Methode weiß eigentlich nicht, welcher Typ in deiner Auflistung ist, anhand der Auflistung, die auch generisch ist, kann das Compiler das aber automatisch heraus finden und sorgt dann im selben Zuge dafür, dass deine Funktion auch den richtigen Typ als Parameter bekommt.
Und der Rückgabetyp der Methode ist wieder eine Auflistung mit genau dem Typen, den Du haben willst.
Wären die Methoden nicht generisch, dann müssten sie untypisiert sein. Dann sähe der Aufruf der Where-Methode z.B. so aus:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
var list = new ArrayList() { 12345 };
var filtered = list.Where(item => (int)item > 3);
var summe = 0;
foreach (var item in filtered)
    summe += (int)item;


Gibst Du der ursprünglichen Liste einen String, fliegt dir der ganze Code um die Ohren.

generisch:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
var list = new List<int>() { 12345 };
var filtered = list.Where(item => item > 3);
var summe = 0;
foreach (var item in filtered)
    summe += item;


Gibst Du der ursprünglichen Liste einen String, lässt dich der Compiler nicht einmal compilieren und zeigt dir direkt wo genau was für ein Fehler ist.

Und ja, ich weiß, dass es auch eine Sum-Methode gibt :D



Insgesamt kannst Du mit Generics Klassen und Methoden schreiben, die unabhängig vom Typ sind und mit gleicher Funktionsweise für unterschiedliche Daten immer wieder verwendet werden können.4
Und oben drauf passt der Compiler auch noch auf, dass Du keinen Mist machst.


Zuletzt bearbeitet von Palladin007 am Sa 02.04.16 14:07, insgesamt 1-mal bearbeitet
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 02.04.16 14:06 
Dein Beispiel ist sicher nicht hilfreich um die Nützlichkeit von Generics zu erkennen sondern zeigt nur wie man den Syntax einsetzt. Ist das übliche Problem Beispiele so einfach zu gestalten ohne das das Beispiel sinn frei wird. In deinem Beispiel, das eigentlich nicht völlig sinn frei ist ;), gewinnst du Typsicherheit. Um den Wert von Typsicherheit zu erkennen braucht es aber etwas Erfahrung. Die jemand der gerade Generics kennen lernt eher selten hat. Und du musst erkennen das sich das so ~ungelenk~ anfühlt weil man andere Techniken weggelassen hat um eben das ganze nicht zu verkomplizieren und genau nur Generics zu zeigen.

Ich versuche mal das zu verkomplizieren um es in was halbwegs sinnvolles zu überführen.
Die Equals Methode erbt jede Klasse von Object die Signatur ist public bool Equals(object obj);. Man vergleicht also object mit object. Also jeden beliebigen Typ mit jedem beliebigen Typ!
Wenn du im Code z.B. einen Integer mit einem String vergleichst wird es nicht knallen das Ergebnis wird aber immer false sein. Du hast also eine Fehlermöglichkeit die man meist nur schwer entdecken kann. Bei deiner Compare Klasse kann das nicht passieren. Siehe

ausblenden C#-Quelltext
1:
2:
3:
int a = 1;            
string b = "Hallo";
a.Equals(b);


Ist völlig valider Code aber eher sinn frei. Equals stammt übrigens aus einer Zeit vor Generics. Sie hätten es später sicher anders gelöst wenn es das mittel der Generic schon gegeben hätte.
Mit deine Klasse zum vergleichen

ausblenden C#-Quelltext
1:
2:
3:
4:
int a = 1;            
string b = "Hallo";
Compare<int> _compare1 = new Compare<int>();
_compare1.Check(a, b);


kann das nicht passieren (du solltest eine 'connot convert string to int' Fehlermeldung bekommen). Es ist eine mögliche Fehlerquelle geschlossen und das schon beim kompilieren.
Der Syntax ist so aber eher ungünstig. Aber sobald du das Bespiel mit Dingen anreicherst die c# auch hergibt (statische Klassen mit statischen Methoden und den Generic von der Klasse an die Methode verschiebst und daraus eien Extension Method macht) kann man da was nutzbares draus machen.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
public static class Compare
{
    public static bool Check<AnyDataType>(this AnyDataType data1, AnyDataType data2)
    {
        return data1.Equals(data2);
    }
}

und dann
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
int a = 1;            
string b = "Hallo";
int c = 2;

a.Check(b);  // kompiliert nicht wegen Typungleichheit
a.Check(c);  // geht

Wie du siehst mußt man jetzt nichtmal den generischen <int> hinschreiben. Den kann der Compiler jetzt da der generic an der Methode hängt aus dem Typen deduzieren. a ist ein int also wird als generischer Typ a angenommen und muß nicht nochmal hingeschrieben werden.

Auch wenn es jetzt in einer nutzbaren Form ist würde man das in seiner allgemeinen Form doch letztlich nicht so machen. Denn der Vergleich mit Equals ist halt dann doch nicht für alle Typen das richtige zum Vergleich auch wenn man jetzt typsicher vergleicht. Ist bei strings am offensichtlichsten. Wie will man vergleichen? Casesensitiv, in welcher Culture, ... egal wie bei string benutzt man eigentlich nie das parameterlose Equals wenn man weiß was man tut.