Announcement

Collapse
No announcement yet.

Merkwürdigkeit bei Vererbungslehre

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Merkwürdigkeit bei Vererbungslehre

    Hallo,

    ich stehe im Moment vor einem für mich unlösbaren Problem. Ich versuche eine Liste zu casten, dazu habe ich mein Problem auf das Wesentlichste reduziert:

    Code:
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace TestLibrary
    {
      public interface IKorb<T> { }
    
      public abstract class Korb<T> : IKorb<T> { }
    
      public class Apfel { }
    
      public class Apfelkorb : Korb<Apfel>  { }
    
      public class Obstiges
      {
        public void KocheEin()
        {
          Apfelkorb apfelKorb = new Apfelkorb();
          Korb<Apfel> apfelKorb2 = (Korb<Apfel>)apfelKorb;
          
          List<Apfelkorb> lstApfelKorb = new List<Apfelkorb>();
          List<Korb<Apfel>> lstApfelKorb2 = (List<Korb<Apfel>>)lstApfelKorb;
        }
      }
    }
    Die rotmarkierte Zeile zeigt meinen Versuch, eine Liste auf den Typ einer anderen zu casten, der Compiler in Visual Studio verweigert das aber. In der grünen Zeile caste ich erfolgreich.
    Ich verstehe nicht, wo das Problem bei der roten Zeile ist.
    Ich bitte euch daher um eure Hilfe. Ich bin für jede Antwort dankbar.

    Viele Grüße

    Andreas Möhlenbrock
    Zuletzt editiert von Andreas Möhlenbrock; 30.05.2007, 15:17. Reason: Klassennamen durch verständlicherere ersetzt

  • #2
    Das compiliert C#:
    IList<Korb<Apfel>> lstApfelKorb2 = (IList<Korb<Apfel>>)lstApfelKorb;

    Der Gund ist, dass einen gernerischen Parameter <T> nicht in ein Object casten kannst. Aber du kann <T> in ein beliebiges Interfaces casten.

    Du kannst z.B. mit einem Where Constraint einschränken, dass IKorb nur Obst aufnehmen kann.

    public class Obst { }
    public abstract class Korb<T> : IKorb<T> where T : Obst { }

    <T> kannst du nun problemlos in Obst casten.

    List<T> hat keinen Where Constraint der sagt alles vom Typ IKorb<Obst> ist Ok.

    Du kannst aber in das Interface IList<T> casten.

    Gruß
    Benjamin

    Quelle:
    http://msdn2.microsoft.com/en-us/lib...enerics_topic5

    Comment


    • #3
      Hallo Benjamin,

      Danke dir für deine Antwort. Zwar kompiliert Visual Studio mit der Änderung des Castens von List<...> auf IList<...> diese Zeile, jedoch wirft dieser Cast-Versuch eine InvalidCastException.

      Viele Grüße

      Andreas
      Zuletzt editiert von Andreas Möhlenbrock; 30.05.2007, 20:59.

      Comment


      • #4
        Hi,

        bin mir ziemlich sicher das das nicht geht.
        Die generischen Klassen sind zwar kompatibel und können gecastet werden das heißt aber nicht das generische Listen dieser Klassen irgendwie in Beziehung stehen würden und dein cast bezieht sich auf die Listen und nicht auf die Listenelemente. Ich gebe zu der Syntax von generischen Klassen ist da unglücklich und verleitet einen dazu das zu übersehen.

        Sie dir mal Methode List<T>.ConvertAll in der MSDN an.
        Die sollte eigentlich genau das gewünschte vollbringen.
        Das enthaltene Beispiel ist übrigens Klasse, sollten sich andere (sprich Codegear) mal'ne Scheibe von abschneiden.


        Gruß
        Ralf

        Comment


        • #5
          Hm,

          ich versuchs nochmal deutlicher. Glaub ich

          List<T> ist nur von Object abgeleitet und implementiert ansonsten nur Interfaces.
          Egal was ich als T nehme es bleibt dabei List<T> bleibt ein Abkömmling ausschließlich von Object.
          Es reiht sich nicht irgendwie in die Vererbungshierarchie von T ein. Wie auch?

          noch mehr Grüße
          Ralf

          Comment


          • #6
            Hallo Ralf,

            einen ganz vorzüglichen Dank! Das hat mir die Augen geöffnet.
            Anbei das damit überarbeitete vollständige Testbeispiel:

            Code:
            using System;
            using System.Collections.Generic;
            using System.Text;
            
            namespace TestLibrary
            {
              public interface IKorb<T> { }
            
              //Klasse darf wegen Converter nicht mehr abstrakt sein
              public class Korb<T> : IKorb<T> { }
            
              public class Apfel { }
            
              public class Apfelkorb : Korb<Apfel>  
              {
                //der Converter
                public static Korb<Apfel> ApfelkorbToKorbApfel(Apfelkorb apfelkorb)
                {
                  return new Korb<Apfel>(); 
                  //von einer abstrakten Klasse hätten keine Instanzen gebildet werden dürfen
                }
              }
            
              public class Obstiges
              {
                public void KocheEin()
                {
                  //wenn direkt zwischen generischen Typen gecastet wird, wird kein Converter benötigt
                  Apfelkorb apfelKorb = new Apfelkorb();
                  Korb<Apfel> apfelKorb2 = (Korb<Apfel>)apfelKorb;
                  
                  //Converter wird benötigt, um bei Listen die Vererbung nachzubilden
                  List<Apfelkorb> lstApfelKorb = new List<Apfelkorb>();
                  //List<Korb<Apfel>> lstApfelKorb2 = (List<Korb<Apfel>>)lstApfelKorb;
                  List<Korb<Apfel>> lstApfelKorb2 = lstApfelKorb.ConvertAll(new Converter<Apfelkorb, Korb<Apfel>>(Apfelkorb.ApfelkorbToKorbApfel));
                }
              }
            }
            Die von Ralf angesprochene Referenz:
            http://msdn2.microsoft.com/de-de/lib...wf(VS.80).aspx

            Danke noch einmal für die Hilfe!

            Viele Grüße

            Andreas Möhlenbrock
            Zuletzt editiert von Andreas Möhlenbrock; 30.05.2007, 23:35.

            Comment


            • #7
              Dein Code ist echt ein Brüller. Danke für das posten. Selten so über Code gelacht.
              Wer so oft Apfelkorb KorbApfel usw. hintereinander schreiben kann ohne mit Knoten in den Fingern zu enden hat eigentlich einen Preis verdient!!

              Trotzdem ein Frage hättest du ihn ApfelkorbToKorbApfel nicht nur casten sollen? Du willst ja keine neue Liste mit neuen Elementen sondern eine mit gecasteten Elementen.

              Also eher so

              Code:
                  public static Korb<Apfel> ApfelkorbToKorbApfel(Apfelkorb apfelkorb)
                  {
                    return (Korb<Apfel>)apfelkorb;
                  }
              Damit dürfte dann Korb auch wieder abstract werden.


              Gruß
              Ralf

              Comment


              • #8
                Hallo Ralf,

                danke dir für den abschließenden Hinweis, die Erzeugung neuer Instanzen, wie in der MSDN angegeben, hatte mich schon stutzig gemacht. Klar, von Apfelkorb kann man ja auch so auf Korb<Apfel> casten.

                Freut mich, dass dir der Code gefällt, wobei ich einen leichten Sarkasmus rauslese, ich denke aber, dass Apfel und Korb sprechendere Namen sind, als deren Bezeichner, die ich tatsächlich verwende. Und wenn zwischen Klassen abgeleitet wird, dass sich dann die Bezeichnung der Klassen ähneln, liegt in der "Natur" der Sache.

                Und Visual Studio passt ja auch auf, dass man nicht durcheinander kommt.

                Danke noch einmal!

                Viele Grüße

                Andreas

                Comment

                Working...
                X