Constraint in die andere Richtung?



  • Guten Morgen,

    Gleich geht es weiter mit meinen grenzwertigen Themen. Heute auf dem Programm: Constraints bei Generics. Normalerweise gibt man als Constraint immer eine Basisklasse an, von welcher der Parameter abgeleitet sein muss, in der Form:

    where T : <base class name>
    where T : <interface name>
    

    Nun habe ich beispielsweise eine Klasse wie folgt:

    class Test<T>
    {
        void Do<U>()
        {
        }
    }
    

    Ist es irgendwie möglich, für U ein Constraint so zu definieren, dass dieses U eine Basisklasse von T sein muss? 😮

    Die Umkehrung des Constraints auf

    where T : U
    

    geht nicht...



  • Ich denke der Thread bezieht sich auf den anderen oder? Wenn dem so ist, bringt dir diese Einschränkung doch eh nichts oder? Dein T ist doch sowieso erst zur Laufzeit bekannt oder irre ich mich da? Oder ist es wirklich ein anderer, exotischer, Anwendungsfall :p



  • Irgendwie versteh ich dies.

    class Test<T>
        {
            void Do<U>() where U : T
            {
            }
        }
    

    Leider zu Faul um so sehen ob es eine Lösung ist 😛 Sonst viel Glück ^^



  • Firefighter schrieb:

    Ich denke der Thread bezieht sich auf den anderen oder? Wenn dem so ist, bringt dir diese Einschränkung doch eh nichts oder? Dein T ist doch sowieso erst zur Laufzeit bekannt oder irre ich mich da? Oder ist es wirklich ein anderer, exotischer, Anwendungsfall :p

    Ja, das hast du richtig erkannt. Ich habe nur gefunden, es wäre doof meine neue Frage in das alte Thema zu stecken. Es ist halt einfach eine Grenzwertige Anwendung, und ich ertappe mich zurzeit immer wieder dabei, wie ich mir wünsche, die C# Generics hätten die Flexibilität von C++ Templates 😉

    Im Übrigen hat ein solches Constraint dennoch einen Nutzen. Wenn ich bereits die Kovarianz (sei es selbst implementierte, oder von .NET 4.0 Generics bereitgestellte Kovarianz - es spielt keine Rolle mehr) von einem ISet<T1>, mit einem zur Laufzeit bekannten T1, nutzen kann, habe ich ein ISet<T2> mit compile-time bekanntem T2, wobei T1 von T2 abgeleitet ist.

    T2 substituiert daher eigentlich mein T1, da T1 für mich gar nie in Erscheinung tritt. Mit einem Parameter U mit Constraint als Basisklasse von T2 habe ich die Möglichkeit eine typsichere (Ko-)Varianz selbst zu definieren, sprich ich kann aus ISet<T2> ein ISet<U> über eine Methode erzeugen.

    Verwirrend, nicht? 😉

    Zeus schrieb:

    Irgendwie versteh ich dies.

    class Test<T>
        {
            void Do<U>() where U : T
            {
            }
        }
    

    Leider zu Faul um so sehen ob es eine Lösung ist 😛 Sonst viel Glück ^^

    Nein, das ist leider nicht, was ich brauche. Ich brauche sowas wie where T : U



  • Man oh meter 😃 Was baust du da abgefahrenes was so eine Flexibilität erfordert?


  • Administrator

    /rant/ schrieb:

    ..., und ich ertappe mich zurzeit immer wieder dabei, wie ich mir wünsche, die C# Generics hätten die Flexibilität von C++ Templates 😉

    Das ist etwas, was man sich meiner Meinung nach schnell abgewöhnen muss, wenn man von C++ kommt. Die Konstrukte werden völlig unbrauchbar. Lieber zum Teil ein paar Klassen oder Interfaces mehr schreiben.

    Zu deinem Problem:
    Ich empfehle dynamic zu verwenden 🙂
    Damit hast du zwar keine Kompilezeitprüfung, aber immerhin eine Laufzeitprüfung und die Komplexität wird reduziert. dynamic wurde ja auch genau für solche Fälle eingeführt.

    Grüssli



  • Damit bist du dann aber komplett auf 4.0 umgestiegen.
    Die Kovarianz hätte man ja noch rauskicken können und das anders designen können, aber mit dynamic holt man sich wirklich die 4.0 Abhängigkeit ins Haus.
    Ist jetzt kein Problem wenn man nur Zielsystem hat wo sowieso 4.0 installiert ist. Ich wollte es zur Sicherheit nochmal sagen 🙂



  • Mit Hilfe einer Erweiterungsmethode sollte es erreichbar sein:

    public class Test<T>
        {
        }
    
        public static class TestExtension
        {
            public static void Do<T, U>(this Test<T> test, U x) where T : U
            { }
        }
    

    Gruss Chris



  • Chrisi_K schrieb:

    Mit Hilfe einer Erweiterungsmethode sollte es erreichbar sein:

    public class Test<T>
        {
        }
    
        public static class TestExtension
        {
            public static void Do<T, U>(this Test<T> test, U x) where T : U
            { }
        }
    

    Gruss Chris

    Sehr gute Idee! 👍 👍



  • GPC schrieb:

    Chrisi_K schrieb:

    Mit Hilfe einer Erweiterungsmethode sollte es erreichbar sein:

    public class Test<T>
        {
        }
    
        public static class TestExtension
        {
            public static void Do<T, U>(this Test<T> test, U x) where T : U
            { }
        }
    

    Gruss Chris

    Sehr gute Idee! 👍 👍

    Nein, Beste Idee für diese Problem 🙂


  • Administrator

    Die Lösung von Chrisi_K hat mindestens einen Schwachpunkt:
    Man muss immer beide Typen angeben. Diese können nicht automatisch deduziert werden.

    Und ob es wirklich die beste Idee für dieses Problem ist, wage ich zu bezweifeln. Empfindet dies niemand sonst als unschönen Hack?

    Ansonsten: Chapeau Chrisi_K!

    Grüssli



  • Ja das empfand ich anfangs auch als "unschön" aber du hast auf jedenfall mehr Compile-Time Sicherheit als mit dynamic und wieso die Schöne Compile-Time Sicherheit gegen was anderes eintausch?:D



  • Dravere schrieb:

    Die Lösung von Chrisi_K hat mindestens einen Schwachpunkt:
    Man muss immer beide Typen angeben. Diese können nicht automatisch deduziert werden.

    Und ob es wirklich die beste Idee für dieses Problem ist, wage ich zu bezweifeln. Empfindet dies niemand sonst als unschönen Hack?

    Ansonsten: Chapeau Chrisi_K!

    Grüssli

    Hmm, was meinst du? Du muss beide einbinden(include/using). Viel unschönere ist, dass die Methode statisch gebunden ist.


  • Administrator

    Firefighter schrieb:

    Ja das empfand ich anfangs auch als "unschön" aber du hast auf jedenfall mehr Compile-Time Sicherheit als mit dynamic und wieso die Schöne Compile-Time Sicherheit gegen was anderes eintausch?:D

    Wie wäre es gegen eine andere compiletime sichere Lösung der eigentlich Aufgabe? Ich sage nicht, dass dynamic besser wäre, sondern dass hier eher etwas am Design nicht stimmt. Vielleicht hat sich da jemand zu sehr an die C++ Templates erinnert und dadurch im Gesamten ein schlechtes Design gewählt.

    Zeus schrieb:

    Hmm, was meinst du? Du muss beide einbinden(include/using).

    Ich meine die Generics:

    class Base { }
    class Derived : Base { }
    
    var t = new Test<Derived>();
    t.Do<Derived, Base>();
    //   ^^^^^^^  ^^^^
    

    Zeus schrieb:

    Viel unschönere ist, dass die Methode statisch gebunden ist.

    Was meinst du damit?

    Grüssli



  • Ach verdammt, daran hab ich nicht gedacht, ja wirklich Hexenzeug 😛

    public class Base { }
    
        public class Sub : Base { }
    
        public static class Extension
        {
            public static void Do(this Base test)
            {
                Console.WriteLine("base");
            }
    
            public static void Do(this Sub test)
            {
                Console.WriteLine("sub");
            }
        }
    
        public class Program
        {
    
            static void Main(string[] args)
            {
                var b = new Base();
                var s = new Sub();
    
                var list = new List<Base>();
                list.Add(b);
                list.Add(s);
    
                foreach (var element in list)
                {
                    element.Do();
                }
                Console.ReadLine();
            }
        }
    

    Mit Extension sollte man vorsichtig sein.


  • Administrator

    Zeus schrieb:

    ...
    

    Mit Extension sollte man vorsichtig sein.

    Achso, dass hast du gemeint. Guter Einwand.

    Grüssli

    PS: Console.ReadKey(true)



  • Dravere schrieb:

    Ich empfehle dynamic zu verwenden 🙂
    Damit hast du zwar keine Kompilezeitprüfung, aber immerhin eine Laufzeitprüfung und die Komplexität wird reduziert. dynamic wurde ja auch genau für solche Fälle eingeführt.

    Omg! Ich wusste gar nicht, dass man so ein schändliches Keyword in die einst so saubere Sprache eingeführt hat. Ich bin enttäuscht - da hämmern alle auf dem alten Visual Basic herum wegen solchen Dingen, und 10 Jahre später ist es der letzte Schrei. Sowas möchte ich auf jeden Fall verhindern.

    Da wir eh gerade von schrecklichen Sprach-Features sprechen: Warum gibt es eigentlich Extension-Methoden, aber keine Extension-Properties? Die wären manchmal auch nützlich :p

    Dravere schrieb:

    Vielleicht hat sich da jemand zu sehr an die C++ Templates erinnert und dadurch im Gesamten ein schlechtes Design gewählt.

    Ja, ich habe in den vergangenen Jahren mehr mit C++ zu tun gehabt als mit C#, und bin insbesondere von Templates sehr angetan. Und hier handelt es sich primär um eine Machbarkeitsstudie - welche jedoch teilweise bereits unter realen geschäftlichen Bedingungen eingesetzt wird. Das Sommerloch ist die ideale Experimentierzeit. Und dabei kommen dann eben solche Projekte raus 😮

    Ich versuche gerade, meine Schnittstellen in ko- und kontravariante Teile aufzuteilen. Mal schauen, wo ich dann hinkomme 🤡


  • Administrator

    /rant/ schrieb:

    Omg! Ich wusste gar nicht, dass man so ein schändliches Keyword in die einst so saubere Sprache eingeführt hat.

    Richtig angewendet ist das Keyword der Hammer. Es ist gerade für dynamischen Code gedacht. Wenn du dir zum Beispiel IronPtyhon anschaust, dann kannst du eine Klasse in IronPython zur Laufzeit generieren und sie dann über dynamic in C# verwenden. Früher hättest du dies nur über aufwändige Reflection machen können, welche von der Laufzeit auch nicht gut optimiert hätte werden können.

    /rant/ schrieb:

    Da wir eh gerade von schrecklichen Sprach-Features sprechen: Warum gibt es eigentlich Extension-Methoden, aber keine Extension-Properties? Die wären manchmal auch nützlich :p

    Wurde soweit ich weiss schon mal überprüft. Hier:
    http://blogs.msdn.com/b/ericlippert/archive/2009/10/05/why-no-extension-properties.aspx

    /rant/ schrieb:

    Dravere schrieb:

    Vielleicht hat sich da jemand zu sehr an die C++ Templates erinnert und dadurch im Gesamten ein schlechtes Design gewählt.

    Ja, ich habe in den vergangenen Jahren mehr mit C++ zu tun gehabt als mit C#, und bin insbesondere von Templates sehr angetan.

    Gilt bei mir auch und ist daher keine Entschuldigung 🤡 😃

    Grüssli


Anmelden zum Antworten