Laden...

Problem: generische Methode und Typeinschränkung

Erstellt von sheitman vor 16 Jahren Letzter Beitrag vor 16 Jahren 3.111 Views
S
sheitman Themenstarter:in
1.047 Beiträge seit 2005
vor 16 Jahren
Problem: generische Methode und Typeinschränkung

Hi, hab hie ein kleines Problem mit einer gernerischen Methode.

Also zunächst einmal hab ich folgende Klassen


public class A {
}

public class B {
}

public class A<T> : A where T : B {
}

nun möchte ich eine Methode in einer anderen Klasse definieren die generisch ist, wobei der Typ aber von meiner A<T> Klasse ist... nur wie gibt man sowas an?


public T DoSomething<T>() where T : A<> {
}

geht nicht, aber ich kann ja auch keinen Typen bei A<> angeben. Gibts da irgendwie nen Platzhalter?

N
335 Beiträge seit 2006
vor 16 Jahren

Hi!

Versuche doch einfach mal das hier:

public T DoSomething<T>() where T : A<T> 
{
}

// Oder 

public T DoSomething<T,U>() where T : A<U> /* evtl. hier noch ein U:B */
{
}


Mfg NeuroCoder

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo sheitman,

ich würde mal tippen auf (ungetestet):

public A<T> DoSomething<A<T>> ()

herbivore

S
sheitman Themenstarter:in
1.047 Beiträge seit 2005
vor 16 Jahren

erstmal vielen dank für die hilfe.
hier die ergebnisse: =)

public A<T> DoSomething<A<T>> ()

Type parameter declaration must be an identifier not a type

public T DoSomething<T>() where T : A<T>

The type 'T' cannot be used as type parameter 'T' in the generic type or method 'A<T>'. There is no implicit reference conversion from 'T' to 'A'.

public T DoSomething<T, U>() where T : A<U>

The type 'U' cannot be used as type parameter 'T' in the generic type or method 'A<T>'. There is no boxing conversion or type parameter conversion from 'U' to 'B'.

public T DoSomething<T, U>() where T : A<U> U : B

sagte er mir nen syntaxfehler

public T DoSomething<T, U>() where T : A<U> where U : B

ließ sich erfolgreich compilieren 🙂
aber finds igendwie blöd das ich das U da halt noch angeben darf 😕
aber da ich das U eh in der methode brauche muß ich mir das nicht erst über reflection aus dem T rausholen. hat also auch nen vorteil^^

falls aber jemand ne lösung findet wie man den typen auf einen generischen typen eingrenzt, bitte melden =)

N
335 Beiträge seit 2006
vor 16 Jahren

Hi!

Der erste Versuch von mir ist Unsinn, da T rekursiv definiert gewesen wäre:
T ist ein A<T> ist ein A< A<T> > ist ein A< A< A<T> > >, etc.

Der zweite Versuch war so gemeint wie dein letzter.
Hier ist es logisch, dass U noch mit angegeben werden muss, da der Compiler sonst keine Chance hat, das U herauszufinden.
Vielleicht wäre es in Spezialfällen denkbar - Schluss über den LValue bei einem Assignment, wobei mir nicht bekannt ist, ob das funktioniert / möglich ist (Stichwort Interfaces als Problem) - aber im Allgemeinen nicht.

Würdest du der Funktion einen Parameter vom Typ T (also A<U>) mitgeben, könnte der Compiler darüber den Typ von U bestimmen.

Meines Wissens gibt es also keine einfachere / schönere Lösung.
(Allerdings hab ich auch nicht soviel Erfahrung mit Generics, dass ich mir ganz sicher sein könnte. Man merkt schon, dass der Template-Mechanismus von C++ mächtiger ist...)

Mfg NeuroCoder

6.862 Beiträge seit 2003
vor 16 Jahren

Man merkt schon, dass der Template-Mechanismus von C++ mächtiger ist...

Auch wenn das wenig zum eigentlichen Problem beiträgt, aber wo bitte sind Templates was Typbeschränkungen angeht mächtiger? Weil man erst gar keine Typen einschränken kann? 🙂 Erst im neuen Standard kommen Concepts mit denen man Typen einschränken kann.

Baka wa shinanakya naoranai.

Mein XING Profil.

N
335 Beiträge seit 2006
vor 16 Jahren

Hallo talla,

Das wäre vielleicht eine eigene Diskussion / Thread Wert.

Man kann auch jetzt schon Constraints auf Template-Argumente setzen:
Zum einen muss ein Typ alle Funktionen bereitstellen, die das Template verwendet.
Zum anderen kann man partielle Spezialisierung ausnutzen und solche Sachen konstruieren:

template <bool> struct Assert;
template <> struct Assert<true> {typedef int type;};

template <class T, class U>
struct SameType {
   enum {value = 0};
};

template <class T>
struct SameType<T, T> {
   enum {value = 1};
};

// Deine Klasse
template <class T>
class CKlasse {
    typedef typename Assert< SameType<T, char>::value || SameType<T, wchar_t>::value>::type ASSERT_TYPE;
};

int main() {
    CKlasse<char> c;  // OK;
    CKlasse<int> i;   // ERROR;
}

(Kopiert aus dem C-Plusplus.de Forum).

Schneller gehts mit Boost:

template < typename T>
   class ConstrainedTemplate
   {
        BOOST_STATIC_ASSERT( boost::is_integral<T>::value );
   };

Zugegeben, Constraints sind kein direktes Feature der Sprache - man muss sie sich selbst herstellen. Aber einmal die Tatsache, dass das mit Templates möglich ist und zum anderen die Möglichkeit Templates zu spezialisieren (auch partiell), machen sie m.E. schon mächtiger als die C#-Generics.
In Bezug auf Meta-Programmierung ist mit Templates schon eine Menge zu machen.

Womit ich nichts gegen Generics sagen will - denn die sind toll 🙂. Aber die Spezialisierung und Möglichkeiten vermisse ich manchmal schon.

Mfg NeuroCoder

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo sheitman,

zum einen war es natürlich Quatsch, dass ich A<T> als Typ-Parameter angegeben habe. Richtig wäre nur T gewesen

public static A<T> DoSomething<T> ()

Allerdings ist dass dann noch etwas zu kurz gesprungen. Was du willst, sollte gehen mit

public static A<T> DoSomething<T> () where T : B

herbivore

S
sheitman Themenstarter:in
1.047 Beiträge seit 2005
vor 16 Jahren

@herbivore

danke, an sowas hab ich noch garnicht gedacht. =)
klappt auch soweit allerdings hab ich noch das kleinen problem:

Cannot implicitly convert type 'A<B>' to 'A'. An explicit conversion exists (are you missing a cast?)

Ne Idee wie ich das ohne Cast wegbekomm, weil der Sinn der generisch machens war eben auch um das casten zu sparen. 😕

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo sheitman

das wundert mich, weil doch jedes A<T> von A erbt oder nicht?. An welcher Stelle passiert der Fehler?

herbivore

S
sheitman Themenstarter:in
1.047 Beiträge seit 2005
vor 16 Jahren

also die gegeben Klassen waren

public class A {
}

public class B {
}

public class A<T> : A where T : B {
}

und die Methode ist

public static A<T> DoSomething<T> () where T : B

so, nun hab ich folgende Klassen

public class C : B {}
public class D : A<C> {}

diese Klasse möchte ich nun halt benutzen

D d = DoSomething<C>();

und hier tritt logischer Weise der Fehler auf, da quasi


A<C> a = DoSomething<C>();
D d = a;

steht und ich wette auch das ein cast zu ner Exception führt da es ja klar ist das ein A<C> nicht unbedingt ein D ist, Vererbung is ja gerade anders rum...

Was ich erreichen wollte ist aber so wie der Aufruf oben.

nochmal was mein Ziel ist:
Ich hab ne generische Klasse geschrieben von der ich erbe, und ich möcht eine Methode auf alle Erben anwenden können, die aber in einer anderen Klasse liegt.

Aber so einfach scheint das wohl nicht. -.- Die Lösung von weiter oben mit 2 Argumenten entspricht dem auch genau letzlich. Is halt nur rigenwdi blöd das man dem Typargument nicht sagen kann das es ein Typ von der generischen Klasse ist.
A geht deshalb nich weil es sei kann das es demnächst kein A mehr gibt sondern nur noch A<B>.

Momentan seh ich als Lösung nur das ich sowohl A und B als Typparameter mitgebe.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo sheitman,

was du willst, geht (so) tatsächlich nicht, denn hier versuchst du ja ein Oberklassen-Objekt (A<C>) an eine Unterklassen-Variable (D) zu zuweisen. Das ist die verkehrte Richtung. Das hat mit Generics nichts zu tun. Das ist einfach falsch. Und da solltest du auf keinen Fall casten, um das doch irgendwie hinzubiegen.

herbivore

S
sheitman Themenstarter:in
1.047 Beiträge seit 2005
vor 16 Jahren

jup, keine angst, das is ja auch net was ich wollte

letzlich war


class A {}
class B {}

class A<T> : A where T : B{}

class B1 : B {}
class B2 : B {}

class A1 : A<B1> {}
class A2 : A<B2> {}

A1 a1 = DoSomething<A1>();
A2 a2 = DoSomething<A2>();

aber das ging so ja nicht 😕

was geht (es compiliert) ist


T DoSomething<T,K>() where T : A<K> where K : B

A1 a1 = DoSomething<A1, B1>();
A2 a2 = DoSomething<A2, B2>();

denk mal damit is das thema auch abgeschlossen =)