Autor Beitrag
ironhaert
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 17



BeitragVerfasst: Do 22.04.10 14:06 
Hallo erst mal..., :-)

ich würde gerne wissen, welche verschiedenen Ansätze und Vorgehensweisen es in C# zum Thema "generische Objekterzeugung und Methodenaufrufe" gibt.
Im weiteren Verlauf ist mein Problem hierzu genauer beschrieben.

Folgendes:
Ich habe mehrere Klassen(K1,K2,K3) die zur Abarbeitung eines bestimmtes Vorgangs dienen.
Alle Klassen haben hierbei die selben Methoden mit den gleichen Parametern (m1(),m2(),m3()).
Nun soll Fall-abhängig ein Instanz einer bestimmten Klasse generisch aufgerufen werden und die Methoden nacheinander aufgerufen werden, wobei der Ablauf immer derselbe ist, erst m1, dann m2, usw..
Um den Aufruf durchzuführen erhalte ich hierbei z.B. ein String, anhand die Instanzbildung entschieden werden soll.

Einfach (switch-case)
Klaro ist ne switch-case-Anweisung wohl die einfachste Möglichkeit. Allerdings müsste hierbei die Anweisung beim Hinzufügen einer weiterer Klasse immer angefasst werden und dies soll es zu vermeiden gelten.

Eine allgemeine Beschreibung würde ausreichen, gerne auch würde ich die Vor- bzw. Nachteiler der genannten Vorgehensweise wissen. Falls mir jemand Codebeispiele oder Spezifischeres mitteilen kann, so darf er das gerne tun.
Vielen Dank, im Voraus,

mfg
ironhaert
danielf
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Do 22.04.10 15:27 
Hallo und :welcome:,

für dieses Problem ist das Factory-Pattern geeignet. Die Klassen (K1,K2,K3) sollten dabei von der selben abstrakten Klasse erben.

Wird eine neue Klasse K4 hinzugefügt programmiert man diese und muss "nur" noch die Factory-Klasse um die neue konkrete Klasse erweitern. An dieser Stelle könntest du auch über [url=]Reflection[/url] anhand der Basisklasse und des Klassennamen vlt. eine dynamische Instantiierung durchführen - das sind Details.

Überlege es dir ... wenn es andere Vorschläge gibt, können wir darüber diskutieren - diese halte ich spontan für einfach, verständlich und gängig.

Gruß
ironhaert Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 17



BeitragVerfasst: Do 22.04.10 15:45 
Hi, danielf und danke erst ma für deine Antwort,

Ich denke, deine Antwort ist genau das, was mir fehlte. Werde das ganze nochmals genauer recherchieren und ausprobieren.

Habe hier schon mal was mit nem Interface versucht, allerdings fehlt genau diese dynamische Instanziierung bzw. das Vorgehen des Factory-Patterns.
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:
49:
50:
51:
52:
53:
54:
55:
56:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestGenericObjectGeneration
{
    class Program
    {
        static void Main(string[] args)
        {
            string classToUse = "K2";

            List<IClassInterface> classList = new List<IClassInterface>();
            K1 k1 = new K1(); K2 k2 = new K2(); K3 k3 = new K3();
            classList.Add(k1); classList.Add(k2); classList.Add(k3);

            foreach (IClassInterface i in classList)
            {
                string t = i.GetType().ToString();
                bool condition = t.Contains(classToUse);
                if (condition)
                {
                    i.m1();
                    i.m2();
                    i.m3();
                }
            }
            Console.Read();
        }
    }
    class K1 : IClassInterface
    {
        public void m1() { System.Console.WriteLine("Method K1m1!"); }
        public void m2() { System.Console.WriteLine("Method K1m2!"); }
        public void m3() { System.Console.WriteLine("Method K1m2!"); }
    }
    class K2 : IClassInterface
    {
        public void m1() { System.Console.WriteLine("Method K2m1!"); }
        public void m2() { System.Console.WriteLine("Method K2m2!"); }
        public void m3() { System.Console.WriteLine("Method K2m2!"); }
    }
    class K3 : IClassInterface
    {
        public void m1() { System.Console.WriteLine("Method K3m1!"); }
        public void m2() { System.Console.WriteLine("Method K3m2!"); }
        public void m3() { System.Console.WriteLine("Method K3m2!"); }
    }
    public interface IClassInterface
    {
        void m1();
        void m2();
        void m3();
    }
}
danielf
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Do 22.04.10 16:01 
Ja so das Gerüst passt.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
        public static IEnumerable<T> CreateInstancesOf<T>(Assembly assembly)
     {
          return CreateInstancesOf<T>(assembly, null);
     }

        public static IEnumerable<T> CreateInstancesOf<T>(Assembly assembly, string namespaceName)
        {
            return (from type in assembly.GetTypes()
                        where ((type.IsClass && type.IsSubclassOf(typeof(T))) 
                        || type.GetInterfaces().Contains<Type>(typeof(T)) &&
                        !type.IsAbstract &&
                        (namespaceName == null || type.Namespace.Equals(namespaceName)))
                    select Activator.CreateInstance(type)).Cast<T>();
        }


Mit diesem Code kannst du von einem Assembly alle Typen laden die zum Beispiel dien IClassInterface implementieren.

Sprich deine Main würde sich auf folgendes reduzieren:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
IEnumerable<IClassInterface> kClasses = CreateInstancesOf<IClassInterface>(Assembly.GetExecutingAssembly());

foreach(var kClass in kClasses)
{
   kClass.m1();
   kClass.m2();
   kClass.m3();
}


Eine Factory Methode könnte so aussehen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
public abstract class KClassFactory
{
    public static IInterfaceClass CreateClass(string name)
    {
         return (from type in assembly.GetTypes()
                        where ((type.IsClass && type.IsSubclassOf(typeof(IInterfaceClass))) 
                        || type.GetInterfaces().Contains<Type>(typeof(IInterfaceClass)) &&
                        !type.IsAbstract && type.Name.equals(name))
                    select Activator.CreateInstance(type)).Cast<IInterfaceClass>().GetEnumerator().Current;
    }
}


wobei:

ausblenden C#-Quelltext
1:
IInterfaceClass k1 = KClassFactory.CreateClass("K1");					


So ungefähr ...
ironhaert Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 17



BeitragVerfasst: Fr 23.04.10 11:53 
Wow,
Super und vielen Dank.
Hast mich ein ganzes Stück weitergebracht.
Code läuft und funktioniert.
ironhaert Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 17



BeitragVerfasst: Mo 26.04.10 12:38 
Hallo nochmals,
ich hänge nun bei einem weiteren Problem.

Da zu meinem Programm alle Klassen K1,K2,K3 usw. jeweils in eigenen assemblys liegen, habe ich keine allgemein assembly, in welcher alle zu erzeugenden Klassen liegen.
Somit reicht es klarerweise nicht mehr nur die auszuführende assembly der Factory mitzugeben, da innerhalb dieser assembly die klassendefinitionen nicht zu finden sind.
Daher müsste ich wohl hier schon am besten alle Assemblies mitschicken, oda so irgendwie und dann die Factory auswählen lassen, welche zu verwenden ist.
Mein Problem hierbei ist, ich weiß net, wie ich die Assemblys dynamisch dementpsrechen wetiergeben kann.

Mein Aufbau ist momentan folgender:
5Projekte: 01_genericAddinCall (Main-Methode, exe), classK1(dll), classK2(dll), classK3(dll), InterfaceClass(dll).

alle meine class-Klassen verweisen natürlich die InterfaceClass, in welchem sich die abstrakte ClassFactoy und eben das Interface befinden.
Meine HauptProjekt(01_genericAddinCall), verweist auf alles und hier befindet sich ja auch der aufruf zur Objekterzeugung.
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
        static void Main(string[] args)
        {
            string classToUse = "K2";

            IEnumerable<IInterfaceClass> kClasses = KClassFactory.CreateInstancesOf<IInterfaceClass>(classToUse, Assembly.GetExecutingAssembly());

            IInterfaceClass IIC = KClassFactory.CreateClass(classToUse);
            foreach (var kClass in kClasses)
            {
                kClass.m1();
                kClass.m2();
                kClass.m3();
            }
            Console.Read();
        }

An dieser Stelle weis ich nun nicht, wie ich weiterkomme bzw. wie ich es einrichten kann, dass am alle bekannten assemblies weitergegeben werden.
Hatte schon versucht mit Assembly.GetExecutingAssembly().GetReferencedAssemblies() die referenzierten assemblys zu übergeben, allerdings werden hier nur "aktive" assemlys aufgeführt, also welche von denen Funktionen bzw. Klassen auch bereits benutz werden.

Eine meiner weiteren Überlegungen war, das ich alle sonstigen Assemblies in einem Ordner ablegen lasse und einfach den Ordner alle Assemlies einlesen lassee odda so.
Wollte allerdings zuerst wissen, ob es hierfür vielleicht einen besseren Ansatz gibt.

Danke und viele grüße,
ironhaert

Ps: Ich hoffe es ist i.O. dass ich diese Frage an meinen alten Post angehängt habe, da ich dachte, da es noch mit dem alten Thema einen Bezug hatte. Ansonsten lösche ich den Post und mach en neuen Thread auf.
danielf
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Mo 26.04.10 13:04 
Es wäre besser gewesen, wenn du ein neues Thema aufgemacht hättest, weil "Wie lade ich ein Assembly aus einer Datei" nichts mit "generische Objekterzeugung und Methodenaufruf" zu tun hat.

In welcher Assembly sich die Klasse befindet kannst du vorher nicht wissen, deshalb bleibt dir nichts übrig, als die verschiedenen Assemblies zu laden Assembly.LoadFrom(<path>) und darin schauen ob du die Klasse laden kannst.

Was anderes fällt mir nicht ein.

Gruß
ironhaert Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 17



BeitragVerfasst: Mo 26.04.10 15:13 
OK. Tut mir leid. Werde es nächstes mal berücksichtigen und en neuen Thread aufmachen.
Und danke für deine rasche Antwort.