Rouven: Generics in C#

Hello,

ich schlage mich gerade mit Generics in C# rum. Seit gestern stehe ich vor einem Problem, das offenbar aus der speziellen Umsetzung in C# resultiert. Ich entnehme gegoogelten Artikeln, dass ich sich um die Covarianz der Generics handelt, die mir das Genick bricht. Schön, hilft mir aber noch nicht.
Folgendes ist das Szenario:
(1) Definition eines generischen Provider Interfaces
interface IDataProvider<D, I>

(2) Definition einer Exception, die als Parameter einen IDataProvider bekommt (throw new MyException(this)).
Hier fordert der Compiler jetzt eine Typisierung ein.
2.1 Aus der Javawelt kommend würde ich einen Wildcard verwenden
MyException(IDataProvider<? extends Object, ? extends Object>)
--> das mag C# dank fehlender Wildcards nicht?!
2.2 gut dachte ich, definierst du ihn halt als Object:
MyException(IDataProvider<object, object>)
--> das funktioniert, allerdings ist ein IDataProvider(string, string) leider in C# kein Subtyp von IDataProvider(object, object)?!
2.3 also definiere ich die Exception selbst als generisch
MyException<D, I>
MyException(IDataProvider<D, I>)
Das funktioniert, zwingt mich jetzt aber beim Werfen der Exception dazu, die Exception entsprechend zu typisieren. Erscheint mir etwas viel für ein
throw new MyException(this).

Wie löst man dann in C# so ein Problem? Viele Artikel befassen sich mit Lösungsansätzen, allerdings kann ich z.B. ein "where T : string" auf Grund der Typrestriktionen nicht verwenden. Was tut man also? Wie kriegt man das handlich? Kann man (sollte man) irgendwo auf nicht-generische Typen casten?

Danke, ich geh jetzt arbeiten, zurück in meine Java-Welt.

MfG
Rouven

--
-------------------
sh:| fo:} ch:? rl:( br:& n4:{ ie:| mo:} va:) js:| de:] zu:| fl:( ss:) ls:& (SelfCode)
Inter Arma Enim Silent Leges  --  Cicero
  1. Hi!

    Nach meinem autodidaktischen Verständnis sehe ich das so:

    Folgendes ist das Szenario:
    (1) Definition eines generischen Provider Interfaces
    interface IDataProvider<D, I>
    (2) Definition einer Exception, die als Parameter einen IDataProvider bekommt (throw new MyException(this)).
    Hier fordert der Compiler jetzt eine Typisierung ein.

    Der Compiler fordert eigentlich immer eine Typisierung ein. Nur, wie soll er sie erkennen, wenn erst zur Laufzeit die Typen aufgrund des übergebenen Objekts bekannt werden? Ohne dass du der Exception-Klasse die Typen verrätst, kann sie nichts typspezifisches mit dem Argument anfangen, jedenfalls nicht ohne Typecast.

    2.1 Aus der Javawelt kommend würde ich einen Wildcard verwenden
    MyException(IDataProvider<? extends Object, ? extends Object>)

    Wo ist dann der Vorteil der starken Typisierung, wenn du nur Object vorgibst? Dann kannst du gleich auf die Generic verzichten und object/Object verwenden.

    2.3 also definiere ich die Exception selbst als generisch
    MyException<D, I>
    MyException(IDataProvider<D, I>)
    Das funktioniert, zwingt mich jetzt aber beim Werfen der Exception dazu, die Exception entsprechend zu typisieren. Erscheint mir etwas viel für ein
    throw new MyException(this).

    Nun weiß aber MyException aufgrund der beim Instantiieren angegebenen konkreten Typen, was Phase ist.

    Danke, ich geh jetzt arbeiten, zurück in meine Java-Welt.

    Nicht alles was erlaubt ist, ist auch gut. Dein verlinkter Artikel verlinkte auf einen über Generic type parameter variance in the CLR, in dem unter anderem zu lesen ist, dass selbst in Java die covarianten Arrays gar nicht hätten enthalten sein sollen.

    Lo!

    1. Hello,

      danke erstmal für die Antwort.

      Wo ist dann der Vorteil der starken Typisierung, wenn du nur Object vorgibst? Dann kannst du gleich auf die Generic verzichten und object/Object verwenden.

      prinzipiell möchte ich den Generic selbstverständlich verwenden und möchte stark typisiert wissen wie der vorliegende Provider arbeitet. Aaaber: in der Exception interessiert mich nur, dass ein beliebiger Provider eine Exception geworfen hat, ein Provider bietet mir z.B. ein getName() an, das ich gerne in die Fehlerbeschreibung aufnehmen möchte. Für das getName ist der Generic allerdings witzlos. Entweder mache ich es jetzt wie beschrieben (Exception generisch) oder ich trenne mein Providerinterface in den generischen und den nicht generischen Teil auf.

      MfG
      Rouven

      --
      -------------------
      sh:| fo:} ch:? rl:( br:& n4:{ ie:| mo:} va:) js:| de:] zu:| fl:( ss:) ls:& (SelfCode)
      There's no such thing as a free lunch  --  Milton Friedman
      1. Hi!

        [...] ich trenne mein Providerinterface in den generischen und den nicht generischen Teil auf.

        Ja, diese Idee hatte ich nach dem Lesen deiner weiteren Erläuterungen auch, bevor ich an diesem Satz ankam. Siehe beispielsweise List<T>, die implementiert mit IList<T> und IList, IEnumerable<T> und IEnumerable sowie ICollection<T> und ICollection auch die generische und nicht genetische Variante.

        Wenn du nicht-generische Methoden aus dem Interface im Normalbetrieb nicht benötigst, kannst du sie ja explizit implementieren, dann siehst du sie nur, wenn du das Objekt als "INonGeneric" betrachtest.

        Lo!

      2. Hallo,

        mein Vorschlag, Trennen: IDataProvider<T1,T2> : IDataProvider

        Cheers, Frank * der grad an Jumbo Frames knabbert *

      3. Hello,

        ...mein Dank an dedlfix und Frank - ich werd's aufteilen...

        MfG
        Rouven

        --
        -------------------
        sh:| fo:} ch:? rl:( br:& n4:{ ie:| mo:} va:) js:| de:] zu:| fl:( ss:) ls:& (SelfCode)
        I will never understand why Germans feel the need to kill trees  --  Arbeitskollege aus UK zum Thema ob eine Dokumentation elektronisch oder auf Papier ausgeliefert wird