C# Verständnisproblem mit der Benutzung der "Generic Type Parameter"

G

Green Mamba

Gast
Hallo,

ich habe eine Methode, die bekommt ein Objekt vom Typ eines Interfaces. Davon leiten mehrere Klassen ab. Innerhalb der Methode möchte ich den konkreten Typ des Objektes herausfinden und als "Generic Type Parameter" weiter verwenden.

Code:
public ISelectiveListItem getObjectMatchingListItem( ISelectiveListItem item)
{
    Type classType = item.GetType();
    
    //So oder so ähnlich sollte der universelle Aufruf aussehen: 
    //Compilermeldung ist: 
    //The type or namespace name 'classType' could not be found (are you missing a using directive or an assembly reference?)  
    AnotherClass.ReadObject<classType> (5);

     //So sähe der Aufruf mit einem konkreten Typ aus, das compiled und funktioniert auch:
     AnotherClass.ReadObject<concreteListItem> (5);
}
Die Mehtode "ReadObject" erwatet eine Klassenbeschreibung, ich habe aber nur ein Objekt vom Typ System.Type zur Verfügung. Wie sähe also die korrekte Syntax aus?

Viele Grüße,
Mamba
 
Zuletzt bearbeitet:
Du könntest eine (abstrakte) Basisklasse für die anderen Klassen schreiben die diese Funktionalität zusammenfasst und dann in der Method einfach in die Basisklasse casten:
Code:
public ISelectiveListItem getObjectMatchingListItem( ISelectiveListItem item)
{
    MyBaseClass base = (MyBaseClass)item;
    List<MyBaseClass> mylist = new List<MyBaseClass>();
    mylist.Add(base);
    base.callFunctionInBaseClass(42);
    ...

Oder Du fragst explizit ab welcher Typ es ist:
Code:
public ISelectiveListItem getObjectMatchingListItem( ISelectiveListItem item)
{
    if (item is MyClass1) {
        MyClass1 myitem = (MyClass1)item;
        List<MyClass1> mylist = new List<MyClass1>();
        mylist.Add(myitem);
        myitem.callFunctionInMyClass1(42);
        ...
 
Zuletzt bearbeitet:
Vielen Dank schonmal fürs hineindenken. :) Deine Lösungsvorschläge würden sicherlich funktionieren, aber ich kann sie in meinem Fall aus folgenden Gründen nicht anwenden:

Zur Lösung 1:
Das impliziert, dass die Funktion ReadObject nicht mehr in "AnotherClass", sondern in der von ISelctiveListItem abgeleiteten Klasse implementiert ist. Das funktioniert aufgrund der äußeren Architektur der Software nicht.

Zur Lösung 2:
Hierfür müsste meine Klasse Kenntnis über alle möglichen Klassen haben, die das Interface ISelctiveListItem implementieren. Diese Abhängigkeit möchte ich vermeiden. Genau darum gehts mir. :)
 
Zuletzt bearbeitet:
Die von dir vorgestellte Lösung ist mit C# nicht möglich!

Die Typ-Parameter von Generics müssen zur Compile-Time bekannt sein. Außer es ist selbst ein Generic.

Du musst das über einen Umweg mit einem Generic machen und den Typ-Parameter auf ein Interface einschränken.

Schau mal hier auf meinem blog, vielleicht hilft dir das ja.
http://www.blog-dotnetprogrammierung.de/?p=277

Btw. Das Wort "abgelittenen" gibt es nicht ;)

Genauso wie Klassen ein Interface implementieren und nicht davon ableiten.
 
toeffi schrieb:
Die von dir vorgestellte Lösung ist mit C# nicht möglich!

Das stimmt nicht.

Code:
using System.Reflection;

Code:
public ISelectiveListItem getObjectMatchingListItem( ISelectiveListItem item )
{
    Type typeOfItem = item.GetType();
    Type typeOfClass = typeof( AnotherClass );
    MethodInfo methodInfo = typeOfClass.GetMethod( "ReadObject" );
    MethodInfo genericMethod = methodInfo.MakeGenericMethod( typeOfItem );
    object[] args = { 5 };
    genericMethod.Invoke( null, args );
}
 
@holy: Korrektes Argument. Ich hab mich da vielleicht etwas unglücklich ausgedrückt.
Ich meinte das es ohne Reflection nicht geht, hab ja auch extra gesagt das der Typ zur Compile-Time bekannt sein muss. Mit Reflection ermittelst du ihn ja zur Laufzeit :)
 
toeffi schrieb:
... hab ja auch extra gesagt das der Typ zur Compile-Time bekannt sein muss. Mit Reflection ermittelst du ihn ja zur Laufzeit :)

Soweit les ich doch nicht, wenn mir der Anfang nicht gefällt :D
 
Vielen lieben Dank für die Unterstützung. Also war mein Gedanke im Ansatz gar nicht so verkehrt, wenn man die Tatsache außer Acht lässt, dass Generic Types zur Compilezeit festehen müssen? :D
Zugegebenermaßen habe ich versucht, mein noch etwas komplizierteres Modell fürs Forum etwas zu vereinfachen.
Eigentlich ist es nämlich so, dass meine Interface ISelctiveListItem dasjenige ist, das von der Generic Class "SelctiveListItem" implementiert wird. Diese hat als Generic Type Parameter die eigentlich konkrete Klasse, die ich an "ReadObject" übergeben möchte. Der Anfang der Methode sieht eigenltich so aus:

Code:
public ISelectiveListItem getObjectMatchingListItem( ISelectiveListItem item)
{
    Type classType = item.GetType();
    Type typeOfItem = classType .GetGenericArguments()[0];

    Type typeOfClass = typeof( AnotherClass );
    MethodInfo methodInfo = typeOfClass.GetMethod( "ReadObject" );
    MethodInfo genericMethod = methodInfo.MakeGenericMethod( typeOfItem );
    object[] args = { 5 };
    genericMethod.Invoke( null, args );
}
Die Typ-Parameter von Generics müssen zur Compile-Time bekannt sein. Außer es ist selbst ein Generic.
Das heißt doch dann eigentlich, dass mein Typ-Parameter selbst ein Generic ist!? Gibts dann noch eine einfachere Lösung ohne Reflection?

Sorry für die vielleicht unbeholfenen Fragen, aber ich beschäftige mich momentan das erste mal ernsthaft mit diesem Thema. :)
 
Zuletzt bearbeitet:
Ok dann zeig uns doch mal den Code den Du wirklich hast. Interessant wäre da das Interface und die Generische Klasse die damit arbeitet. Vielleicht können wir dir was zusammen zaubern :)
 
Sag bitte nicht immer ableiten, dass ist verwirrend :) toeffi hat's doch schon erwähnt, es heißt implementieren :D Klassen leiten von Klassen ab, aber nicht von Interfaces - dazwischen gibt's auch einen riesen Unterschied.

Ich versteh dein Problem auch nicht wirklich :) Lass mal den Code sehen :)
 
Den Originalcode kann ich hier leider nicht veröffentlichen, daher kann ich lediglich versuchen die Erklärung wie die Konstellation aussieht, so umfassend und verständlich wie möglich zu liefern. Sorry.
 
Dann schieß ich einfach mal ins Blaue :)

Du hast mehrere generische Klassen, die alle das Interface implementieren?
Weiterhin hast du eine List<ISelectiveListItem> in der du alle Objekte der Klassen sammelst?
An Hand des Namens der Methode vermute ich mal, dass du nun ein Item des gleichen Types wie das übergebene Argument haben möchtest, dass eine bestimmte Bedingung erfüllt?

Wie gesagt, geraten :D
 
Zuletzt bearbeitet:
Das Problem ist, dass ISelectiveListItem keinerlei Typinformationen hat die der Compiler prüfen könnte.
Was ist z. B. wenn du folgende Klasse hast:
Code:
public class BadListItem : ISelectiveListItem
{
    // ...
}
Das ist eine gültige Implementierung von ISelectiveListItem hat aber keinen generischen Typ-Parameter. Deine "getObjectMatchingListItem" Methode würde mit einer Instanz dieser Klasse übrigens auch nicht funktionieren.

Die naheliegenste Lösung wäre das Interface generisch zu machen:
Code:
public interface ISelectiveListItem<T>
{
    // ...
}
In wie weit das aber sinnvoll ist, ist vom konkreten Fall abhängig und ohne Code nicht zu beurteilen.
 
Es geht auch ohne Reflection, wie ich bereits anfangs schrieb, hier ein Beispiel dazu:

Code:
public abstract class Mother {
	public Mother() { }

	public void MotherFunc() {
		Console.WriteLine("MotherFunc");
	}
}

public class Daugther: Mother, IComparable { // Interface IComparable nur als Beispiel
	public int Value;
	public Daugther() { }

	public void DaugtherFunc() {
		Console.WriteLine("DaugtherFunc");
	}

	// IComparable Interface
	public int CompareTo(object obj) {
		if (obj is Daugther)
		return Value.CompareTo(((Daugther)obj).Value);

		throw new ArgumentException("object is not a Daugther");
	}
}

public class Test {
	[STAThread]
	public static void Main(String[] args) {
		Daugther Tiffany = new Daugther();
		Test.TestCall(Tiffany);
	}

	public static void TestCall(IComparable interfacecall) {
		if (interfacecall is Mother) {
			Mother mothercalled = (Mother)interfacecall;
			mothercalled.MotherFunc();
			if (mothercalled is Daugther) {
				Daugther daugthercalled = (Daugther)interfacecall;
				daugthercalled.DaugtherFunc();
			}
		}
	}
}
 
lynxx schrieb:
Es geht auch ohne Reflection, wie ich bereits anfangs schrieb, hier ein Beispiel dazu
In deinem Beispiel muss aber die Methode "TestCall" wissen welche Implementierungen vom Interface es gibt. Wenn du also einen neue Implementierung z. B. "Son" erstellst musst du das in "TestCall" nachziehen. Außerdem je mehr Implementierungen es gibt, desto unübersichtlicher wird dein Code.

Wenn Performance wirklich relevant ist, ist deine Lösung gangbar. Aber i. d. R. ist die Reflection-Methode (mit entsprechender Fehlerbehandlung) der bessere, weil wartbarere Weg.
 
Ich persönlich würde keine der beiden Lösungen verwenden, weil die Methode ja eigentlich nur ein Interface vorschreibt, sobald eine Klasse die Funktion mit der Reflection-Lösung aufruft die wirklich nur das Interface implentiert und keine ReadObject-Methode hat knallt es. :D

Aber ohne den genaueren Aufbau der Klassenstrukturen zu kennen ist eine bessere Definition nicht möglich, besser wäre auf jeden Fall wenn beim Funktionsaufruf klar ist welche Funktionalität ein Objekt haben muss, bzw innerhalb der Funktion genutzt wird.
 
Naja, mit dem Interface schreibst du vor, welche Funktionalität dein Object haben muss, ein Vertrag eben. ReadObject ist static und sollte nach Beschreibung von Green Mamba mit allen Objekten, die das Interface implementieren zurecht kommen.

Aber du hast recht. Das Hauptproblem hier ist nicht die Art und Weise der Implementierung, sondern dass niemand so recht weiß, was eigentlich gemacht werden soll. Vermutlich gibt es eine ziemlich simple Lösung, aber ohne weitere Infos ist's halt schwer.
 
Zurück
Oben