Autor Beitrag
UGrohne
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: So 01.10.06 15:51 
Hallo,
eine etwas schwierigere/komplexere Frage: Ich habe eine abstrakte Basis-Klasse, von der ich weitere Klassen ableite. Jede der abgeleiteten Klassen bekommt einen static-String "Identifier".
In einer Methode meiner Komponente verwende ich nun einen String. Und jetzt möchte ich anhand dieses Strings eine Instanz derjenigen Klasse erzeugen, die diesen String als Identifier hat. Dazu muss ich alle Nachfahren meiner Basis-Klasse bekommen.

Geht das z.B. über Reflection irgendwie?

Grüße,

Uwe
UGrohne Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: So 01.10.06 19:24 
Alternativ würde auch ein anderes Vorgehen möglich sein:

1. Ich registriere beim Erstellen der Klasse eine Liste von Klassen, die diesen Identifier haben, der Identifier ist ja in einer Basisklasse vorhanden.
2. Der "Benutzer" kann über dieselbe Methode weitere Klassen für die Verarbeitung registrieren.
3. Ich gehe die Liste der Klassen durch, schaue welche Klasse für die aktuelle Operation verwenden werden muss, erzeuge eine Instanz davon und fertig.

Wie wäre so ein Vorgehen am besten zu realisieren? Am Schwierigsten ist für mich die Frage, wie ich die Liste der Klassen am besten fülle, mit welchen Typen?
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 02.10.06 13:27 
Für die Liste würde ich ein Dictionary<string, Type> benutzen.
Um den Identifier zu ermitteln, könntest du Reflection benutzen (Type.GetProperty/PropertyInfo.GetValue) oder - was vielleicht einfacher wäre - den Identifier direkt beim Registrieren angeben.
UGrohne Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Mo 02.10.06 17:03 
Das ist schon mal ein sehr guter Tipp, vielen Dank. Soweit funktioniert es jetzt mal. Nur das Erzeugen der entsprechenden Klasse scheint nicht so ganz einfach zu sein, ich bekomme immer einen Fehler:
Zitat:
[Klasse] ist keine GenericTypeDefinition. MakeGenericType darf nur für einen Typ aufgerufen werden, für den Type.IsGenericTypeDefinition auf "true" festgelegt ist.

Folgenden Code verwende ich:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
System.Type type;
Classes.TryGetValue(node.Name,out type);
Type[] typeArgs = {};
Type constructed = type.MakeGenericType(typeArgs); //<--- Fehler
UpdateItem item = (UpdateItem) Activator.CreateInstance(constructed);

Jetzt weiß ich gerade nicht mehr weiter.

//EDIT: Für alle, die es sonst noch interessiert, hier die Quelle für den Code: msdn2.microsoft.com/...ibrary/b8ytshk6.aspx
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 02.10.06 22:56 
Die Klasse besitzt wohl keine Typparameter? Bei nicht-generischen Klassen kannst du sofort den Acitvator anwenden.
[add]Oder ist es ein konstruierter Typ?
Zitat:
A constructed type such as Base<int, V> is useful when emitting code, but you cannot call the MakeGenericType method on this type because it is not a generic type definition. To create a closed constructed type that can be instantiated, first call the GetGenericTypeDefinition method to get a Type object representing the generic type definition and then call MakeGenericType with the desired type arguments.

[/add]
UGrohne Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Di 03.10.06 12:10 
Super, wenn ich das nicht über MakeGeneric mache, funktioniert es.
ausblenden C#-Quelltext
1:
UpdateItem item = (UpdateItem) Activator.CreateInstance(type);					

Mit den generischen Klassen bin ich nicht wirklich vertraut, daher hab ich da nicht so durchgeblickt. Meine Klassendefinition sieht folgendermaßen aus:
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:
25:
    interface IUpdateItem
    {
        bool UpdateAvailable();
    }
    abstract class UpdateItem
    {
        public static string Identifier;
        public XmlNode node;
        protected string localVersion;
        protected string foreignVersion;
        protected bool compressed;
    }

    class UpdateFile: UpdateItem, IUpdateItem
    {
        public new static string Identifier = "File";
        private string localPath;       //Lokaler Pfad für Zieldatei
        private string localFilename;   //Lokaler Dateiname
        private string foreignFile;     //URL für Internet-Datei

        public bool UpdateAvailable()
        {
            return false;
        }
    }

Ich erzeuge hier erst Instanzen von UpdateFile, vielleicht liegt es daran, dass ich ein Interface noch implementiere?
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Di 03.10.06 13:51 
Vielleicht sollte ich mal wieder meine Brille putzen, aber ich sehe da keine generischen Klassen ;) . MakeGenericType benötigst du nur, um Typparameter wie in List<T> mit echten Typen zu ersetzen. Denn List<T> lässt sich schlecht instanziieren, List<int> sehr wohl.
UGrohne Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Di 03.10.06 14:19 
user profile iconKhabarakh hat folgendes geschrieben:
Vielleicht sollte ich mal wieder meine Brille putzen, aber ich sehe da keine generischen Klassen ;) . MakeGenericType benötigst du nur, um Typparameter wie in List<T> mit echten Typen zu ersetzen. Denn List<T> lässt sich schlecht instanziieren, List<int> sehr wohl.

Ach, ok. Ich sollte mir generische Klassen wohl mal ansehen. Hab das nur nach dem Beispiel im MSDN gemacht *g*.
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Mo 27.11.06 23:14 
Kommt vllt. ein wenig spät, aber ich schaue hier nicht mehr so oft rein wie ihr es eigentlich verdient :oops: (sehr geiles phpBB Forum, btw).

Normalerweise macht man sowas in .Net mit Metadaten, in dem Fall dürften Attribute ganz praktisch sein.
Dadurch kannst du direkt in deinem Code deine Klassen annotieren ohne ihr Laufzeitverhalten oder die Art wie man sie konsumiert/aufruft auch nur im kleinsten zu beeinflussen.

Du könntest ein kleinen Attribut nehmen wie das hier:
ausblenden Chrome Quelltext
1:
2:
3:
4:
5:
6:
7:
type
  [AttributeUsage(AttributeTargets.Class or AttributeTargets.Interface)]
  IdentifierAttribute = public class(Attribute)
  public
    property ID : String;
    constructor; empty;
  end;


Nun nehme man eine weitere ClassLib und schmeiße 3 Klassen hinein:
ausblenden Chrome Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
type
  [Identifier(ID := 'A')]
  Class1 = public class 
  end;

  [Identifier(ID := 'B')]
  Class2 = public class 
  end;

  Class3 = public class 
  end;

Und damit C# im C#-Forum nicht zu kurz kommt, gibbet es da etwas mehr Sauce: *g*

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:
static T GetAttribute<T>(Type type)
  where T : Attribute
{
  T[] attributes = type.GetCustomAttributes(typeof(T), falseas T[];
  if (attributes.Length > 0)
    return attributes[0];
  else
    return null;
}

static void Main(string[] args)
{
  Assembly classLib2 = Assembly.Load("UweSample.ClassLib2");

  foreach (Type loadedType in classLib2.GetExportedTypes())
  {
    IdentifierAttribute id = GetAttribute<IdentifierAttribute>(loadedType);

    if (id != null)
      Console.WriteLine("{0} has the ID {1}", loadedType, id.ID);
  }
}