Autor Beitrag
thomas_danecker
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Di 14.03.06 16:10 
Eine "einfache" Klasse:
ausblenden C#-Quelltext
1:
2:
3:
4:
public class Bar<T>
    where T : Bar<T>
{
}


Dieser code kompiliert einwandfrei doch jeder wird sich die Frage stellen: Wie erstellt man eine Instanz von dieser Klasse?
Schon mal vorweggenommen: Es gibt eine Möglichkeit!

Aber so nicht:
ausblenden C#-Quelltext
1:
Bar<Bar<Bar<Bar.....>>>>					


So auch nicht:
ausblenden C#-Quelltext
1:
2:
Type t = typeof(Bar<>);
t.MakeGenericType(t.MakeGenericType(...));


Schon jemand eine Idee?

Man braucht noch etwas Hilfe:
ausblenden C#-Quelltext
1:
2:
3:
class Foo : Bar<Foo>
{
}


So, jetzt gehts:

ausblenden C#-Quelltext
1:
Bar<Foo> bar = new Bar<Foo>();					


Wie man sieht kann man sogar die Klasse selbst als generischen Parameter verwenden!

Moderiert von user profile iconChristian S.: C#-Tags hinzugefügt
Moderiert von user profile iconChristian S.: Topic aus C# - Die Sprache verschoben am Di 14.03.2006 um 15:36
Moderiert von user profile iconTh69: Topic aus Neue Einträge / Hinweise / etc. verschoben am Do 16.05.2019 um 09:02
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Di 14.03.06 16:44 
user profile iconthomas_danecker hat folgendes geschrieben:
So, jetzt gehts:
Bar<Foo> bar = new Bar<Foo>();Wie man sieht kann man sogar die Klasse selbst als generischen Parameter verwenden!
Es geht auch einfach so: Foo foo = new Foo();
Das ganze nennt sich rekursive naked constraint und ist vital wenn man die Klasse selbst als Typenparameter einer anderen Klassen übergeben will/muss.
So kann man interfaces bauen, die zwar poperties besitzen, die durch dieses Interface festgelegt sind, aber eine Klasse sein können, die das interface implementiert:
ausblenden volle Höhe 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:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
interface IMiep<T, D> 
  where T: IMiep<T, D>
  where D: IMööp<D, T>, new()
{
  D Mööp { get; }
}

interface IMööp<D, T>
  where D: IMööp<D, T> 
  where T: IMiep<T, D> 
{
  T Miep{ get; set; }
}


class Miep<T, D> : IMiep<T, D>
  where T: Miep<T, D>
  where D: Mööp<D, T>, new()
{
  D mööp = new D();

  public D Mööp
  {
    get{return mööp;}
  }

  public Miep()
  {
    mööp.Miep = this;
  }
}

class Mööp<D, T> : IMööp<D, T>
  where D: Mööp<D, T> 
  where T: Miep<T, D> 
{
  T miep;
  public T Miep
  {
    get{return miep;}
    set{miep = value;}
  }
}

class TestMiep : Miep<TestMiep, TestMööp>
{}
class TestMööp : Mööp<TestMööp, TestMiep>
{}

Sowas baut man sich tief in generische Libs mit einem dritten Typen, der dann in den jeweiligen Klassen den Wert hält. Auf die Art kann man mit einem Satz Code plötzlich zig Klassen bedienen. Öffentlich sichtbar macht solch kranke Konstrukte eigentlich nie. ;)
Der Vorteil liegt darin, dass dir Typenparameter keine Laufzeitkosten bescheren, alles wird zur Compile/JIT-Zeit aufgelöst.
Nicht alles was auf den ersten Blick sinnlos erscheint muss auch tatsächlich sinnlos sein. Gerade für dynamisch generierten Code ist sowas ein echter Lebensretter...

Das klassiche Beispiel dürfte IComparable<T>, IEquatable<T>,... sein.
Ohne diese rekursiven constraints müsste man einen int auf object boxen um ihn in generics vergleichen zu können. Mit geht plötzlich das:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
class SomeList<T> : List<T>
  where T: IEquatable<T>;
{
  public IEnumerable<T> FindMatching(T value)
  {
     foreach(T item in this)
     {
       if (item.Equals(value))
         yield return item;
     }
  }
}