Entwickler-Ecke
C# - Die Sprache - typisierter Klassenzeiger
Robert_G - Fr 24.03.06 00:21
nope, MetaClasses sind kein Teil der CLR. Somit gibt's die auch nicht in C#. :(
Wenn es dir nur darum geht Instanzen zu erzeugen wäre dass hier möglich:
C#-Quelltext
1: 2: 3: 4: 5:
| T CreateInstance<T>() where T : new() { return new T(); } |
Man beachte die new() -constraint, welche für T einen öffentlichen, parameterlosen Contructor vorschreibt.
Willst du dem Constructor Parameter übergeben hast du die Qual der Wahl...
Es gibt da die Klasse Activator. Die it einfach zu benutzen, aber muss für jeden Call den richtigen Constructor finde -> laaaahmarschig :(
Du kannst dir eine generische Factory bauen, die den nötigen Il Code für einen call auf den richtigen Constructor generiert.
Da generische Klassen für jeden unterchiedlichen Typparameter einen eigenen Satz von statischen Variablen haben, ist es sehr easy den genierten Code immer wieder benutzen zu können.
Da ich gerne mit Dynamic Methods spiele, habe ich dir mal ein kleines Beispiel gebaut.
Die Methode zum Generieren des constructor Wrappers habe ich absichtlich unabhängig von Parameteranzahl und -typ gemacht, so kannst du sie einfach selbst benutzen. :)
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:
| static class Factory<T> { public static T CreateInstance(int someValue) { return constructor(someValue); }
delegate T Constructor(int someValue);
static Constructor constructor = InitialConstructor;
static T InitialConstructor(int someValue) { constructor = CreateConstructorCall(new Type[] { typeof(int) });
return constructor(someValue); }
static Constructor CreateConstructorCall(Type[] parameterTypes) { DynamicMethod method = new DynamicMethod("ConstructorCall", typeof(T), parameterTypes, typeof(Factory<T>));
ILGenerator codeGen = method.GetILGenerator();
for (byte argument = 0; argument < parameterTypes.Length; argument++) { codeGen.Emit(OpCodes.Ldarg_S, argument); }
codeGen.Emit(OpCodes.Newobj, typeof(T).GetConstructor(parameterTypes)); codeGen.Emit(OpCodes.Ret);
return (Constructor)method.CreateDelegate(typeof(Constructor)); } } |
Dynamic methods laufen unter "light weight code generations". D.h. der Code sollte im einstelligen ms-Bereich generiert werden.
Jeder weitere Call ist vergleichbar mit einem normal kompilierten statischen Methodenaufruf, auf deutsch: sackschnell. ;)
Hier noch ein bisschech Beispiel code, der zeigen soll wie und das es funktioniert.
Das Interface war nicht notwendig, aber dadurch muste ich die Test methode nur einmal schreiben. :zwinker:
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: 49: 50: 51:
| interface ISomeValue { int SomeValue { get;} }
struct Struct : ISomeValue { int someValue;
public int SomeValue { get { return someValue; } }
public Struct(int someValue) { this.someValue = someValue; } }
class Class : ISomeValue { int someValue;
public int SomeValue { get { return someValue; } }
public Class(int someValue) { this.someValue = someValue; } }
class Program { static void Main(string[] args) { Test<Class>(1); Test<Struct>(2); }
static void Test<T>(int someValue) where T : ISomeValue { T test = Factory<T>.CreateInstance(someValue);
Console.WriteLine("{0}: {1}", test, test.SomeValue); } } |
Bei Fragen einfach fragen... :mrgreen:
r2c2 - Fr 24.03.06 19:44
Robert_G hat folgendes geschrieben: |
nope, MetaClasses sind kein Teil der CLR. Somit gibt's die auch nicht in C#. :(
|
Schade. :( Hoffentlich stoße ich nicht auf noch mehr Fehlende Feartures...
Zitat: |
Willst du dem Constructor Parameter übergeben hast du die Qual der Wahl...
|
Jo, will ich. N string...
Zitat: |
Du kannst dir eine generische Factory bauen, die den nötigen Il Code für einen call auf den richtigen Constructor generiert.
Da generische Klassen für jeden unterchiedlichen Typparameter einen eigenen Satz von statischen Variablen haben, ist es sehr easy den genierten Code immer wieder benutzen zu können.
Da ich gerne mit Dynamic Methods spiele, habe ich dir mal ein kleines Beispiel gebaut.
Die Methode zum Generieren des constructor Wrappers habe ich absichtlich unabhängig von Parameteranzahl und -typ gemacht, so kannst du sie einfach selbst benutzen. :)
|
Ui. :shock: Vielen Dank! :zustimm:
Zitat: |
Bei Fragen einfach fragen... :mrgreen:
|
Jo, mach ich auch gleich mal. Hab mir deinen Code angeguckt. Hat zwar ne Zeit lang gedauert, aber jetzt hab ichs mehr oder weniger verstanden. Hab deinen Code mal rekommentiert und geschrieben, was IMHO da passiert.
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: 49:
| static class Factory<T> { public static T CreateInstance(int someValue) { return constructor(someValue); }
delegate T Constructor(int someValue);
static Constructor constructor = InitialConstructor; static T InitialConstructor(int someValue) { constructor = CreateConstructorCall(new Type[] { typeof(int) });
return constructor(someValue); }
static Constructor CreateConstructorCall(Type[] parameterTypes) { DynamicMethod method = new DynamicMethod("ConstructorCall", typeof(T), parameterTypes, typeof(Factory<T>));
ILGenerator codeGen = method.GetILGenerator();
for (byte argument = 0; argument < parameterTypes.Length; argument++) { codeGen.Emit(OpCodes.Ldarg_S, argument); }
codeGen.Emit(OpCodes.Newobj, typeof(T).GetConstructor(parameterTypes)); codeGen.Emit(OpCodes.Ret);
return (Constructor)method.CreateDelegate(typeof(Constructor)); } } |
Hab ich das so richtig verstanden?
mfg
Christian
P.S.: System.Reflection.Emit gefällt mir. Muss ich mir mal genauer angucken... :wink:
Robert_G - Fr 24.03.06 20:50
r2c2 hat folgendes geschrieben: |
Hab ich das so richtig verstanden? |
Ich fürchte nicht, aber vllt habe ich dich falsch verstanden. ;)
Ich nehme mal die wichtigsten Stellen und versuche es mal am Ablauf des ersten und zweiten Calls auf CreateInstance zu erklären. Vllt. hilft's dem nächsten Leser ja auch. :)
Wie du siehst ist CreateInstance selbst nur ein Wrapper um den Delegate.
C#-Quelltext
1: 2: 3: 4:
| public static T CreateInstance(int someValue) { return constructor(someValue); } |
Der Delegate zeigt ersten Aufruf noch auf InitialConstructor:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| static Constructor constructor = InitialConstructor;
static T InitialConstructor(int someValue) { constructor = CreateConstructorCall(new Type[] { typeof(int) });
return constructor(someValue); } |
InitialConstructor hat die richtige Signatur, deshalb kann man ihn überhaupt dem Delegate zuweisen.
Er wird CreateConstructor aufrufen, damit dem Delegate eine fertig kompilierte, neue Methode zugewiesen wird.
Danach ruft er selbst den Delegate auf um den Wert zurückzugeben. Es soll ja nach außen keinen funktionialen Unterschied zwischen dem ersten und weiteren Calls auf CreateInstance geben. :zwinker:
Wie man in der ersten Zeile von InitialConstructor sieht zeigt der Delegate jetzt auf die neue Methode.
Jeder weitere Call auf CreateInstance wird also nicht mehr InitialConstructor sondern direkt die eben generierte Methode aufrufen.
Ich hätte es auch so schreiben können[meta]also ohne InitialConstructor[/meta]...
C#-Quelltext
1: 2: 3: 4: 5: 6: 7:
| public static T CreateInstance(int someValue) { if(constructor == null) constructor = CreateConstructorCall(new Type[] { typeof(int) });
return constructor(someValue); } |
... aber ehrlich gesagt finde ich es wesentlich eleganter, den Delegate initial auf eine Methode zu setzen, die ihn vorbereitet und zurechtbiegt. Auf die Art spart man sich bei jedem weiteren Call einen Vergleich und einen Sprung[meta]keine if clause nötig[/meta]. :)
Zitat: |
P.S.: System.Reflection.Emit gefällt mir. Muss ich mir mal genauer angucken... :wink: |
Jupp, ist ziemlich cool. :)
r2c2 - Sa 25.03.06 10:43
Ah! Jetzt wird doch vieles klarer. Danke!
mfg
Christian
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!