none
Erstellen einer Struktur mit direkter Zuweisung, initalisierung RRS feed

  • Frage

  • Hallo NG

    Beim Datentyp Integer handelt es sich ja um eine Struktur und ich kann sie wie Int16 xy = 22; anwenden.

    Nun möcht ich eine eigene Struktur erstellen, welche auch so eingesetzt werden kann, also MeineStuct xy = "Hallo Welt"; und nicht MeineStruct xy = New MeineStruct("Hallo Welt");

    Was für eine Methode oder was auch immer muss in der Struktur dafür implementiert werden?

    Vielen Dank und Gruss

    Andreas

    Montag, 18. Oktober 2010 12:42

Antworten

  • Selbstverständlich geht so etwas. Zu diesem Zweck kann man einen impliziten Konvertierungsoperator erzeugen, etwa so:

     public struct Path
     {
       private String path;
    
       public Path(String newPath)
       {
         this.path = newPath;
       }
    
       public static implicit operator Path(String newPath) 
       {
         return new Path(newPath);
       }
     }
    

    Anschließend kann man die Zuweisung genau so durchführen, wie Du das möchtest. Sie verwendet dann den Konstruktor:

    Path p = "test";
    
    Gibt es einen Grund dafür eine Struktur zu verwenden? Hierdurch wird ein Wertetyp erzeugt. Der Vorgang funktioniert zumindest auf die gleiche Art auch mit einer Klasse.

    Montag, 18. Oktober 2010 14:06
  • Hallo Andreas,

    die verkürzte Schreibweise für Integer (und andere Builtin Types) ist letztendlich semantischer Zuckerguß,
    den der Compiler darüber legt.

    Es im allgemeinen zu erwarten würde viele Probleme verursachen.
    Was Eric Lippert in einer kleinen Artikel-Reihe vor kurzem zu einem ähnlichen "Wunsch" ausgeführt hat:

    Ambiguous Optional Parentheses, Part One
    Ambiguous Optional Parentheses, Part Two
    Ambiguous Optional Parentheses, Part Three

    Im letzten Teil kommt ein prefix unary operator an die Reihe, der hier ins Szenario passt.

    Zu Deinem Beispiel: Gehen täte das mit Bordmitteln.

    using System;
    
    namespace ElmarBoye.Samples.Code
    {
      public struct Pfad
      {
        public Pfad(string value)
        {
          this._value = value;
        }
    
        private string _value;
        public string Value
        {
          get { return this._value ?? ""; }
          set { this._value = value; }
        }
    
        public static implicit operator string(Pfad p)
        {
          return p.Value;
        }
    
        public static implicit operator Pfad(string value)
        {
          return new Pfad(value);
        }
    
        public override string ToString()
        {
          return this.Value;
        }
    
        // TODO: Equals, GetHashCode
    
        
        // Funktionstest
        internal static void TestIt()
        {
          Pfad meinPfad = "1. Pfad";
          Pfad appPfad = meinPfad;
          Pfad userPath = appPfad + ".Noch mehr Pfad";
    
          Console.WriteLine("Mein: {0}, App: {1}, User: {2}", meinPfad, appPfad, userPath);
        }
      }
    }
    
    

    Wobei ich das nun nicht unbedingt im realen Leben täte ;-)

    Funktionieren tut es hier nur, weil wir einen skalaren (String) Wert verpackt haben -
    spätestens mit der zweiten Eigenschaft wäre das Spielchen zu Ende.

    Am Ende ist doch Objekt-Initialisierer der universellere Weg, auch wenn man
    dafür ein, zwei Zeichen (mit Hilfe von Intellisense) mehr tippen muß -
    und der Exkurs bei Eric Lippert beendet werden sollte.

    Gruß Elmar

    Montag, 18. Oktober 2010 14:43
    Beantworter
  • Hallo Andreas,

    C# ist eine statisch typisierte Sprache. Bei einer Zuweisung muss sichergestellt werden, dass der Laufzeittyp des zugewiesenen Objekts direkt zuweisbar oder zumindest konvertibel ist. Obwohl man einen String implizit parsen und einer Struktur zuweisen kann, bin ich - wie Elmar - der Meinung dass die Einsatzszenarien dafür relativ begrenzt sind.

    using System;
    using System.ComponentModel;
    
    namespace StaticallyTyped
    {
      class Program
      {
        static void Main(string[] args)
        {
          Address a = "Schmoll-Str. 13;79999;Testhausen";
          Console.WriteLine(a.City);
          Console.ReadKey(true);
        }
      }
    
      [TypeConverter(typeof(StringToAddressConverter))]
      struct Address 
      { 
        public string Street {get; set;}
        public string Pob { get; set; }
        public string City { get; set; }
    
        public static implicit operator Address(string s)
        {
          return (Address) TypeDescriptor.GetConverter(typeof(Address)).ConvertFrom(s); 
        }
      }
    
      public class StringToAddressConverter : TypeConverter
      {
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
          return base.CanConvertTo(context, destinationType);
        }
    
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
          if (sourceType == typeof(string))
            return true;
          else
            return base.CanConvertFrom(context, sourceType);
        }
    
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
          if (value is string)
          {
            string stringValue = (string)value;
            string[] parts = stringValue.Split(new char[] { ';' });
    
            Address a = new Address() { Street = parts[0], Pob = parts[1], City = parts[2] };
            return a;
          }
          else
            return base.ConvertFrom(context, culture, value);
        }
      }
    }
    
    

     

    Gruß
    Marcel

    Montag, 18. Oktober 2010 18:23

Alle Antworten

  • Hallo Andreas,

    ich befürchte, das wird nicht funktionieren. Dazu müsstest du den Zuweisungsoperator (=) überladen, was in .net nicht möglich ist.

    Aber ehrlich gesagt verstehe ich auch gar nicht warum du einen direkten Konstruktoraufruf im Code vermeiden möchtest?

    Viele Grüße
    Holger M. Rößler

    • Bearbeitet Holger M. Rößler Montag, 18. Oktober 2010 13:17 Rechtschreibung und kürzer
    Montag, 18. Oktober 2010 13:10
  • Hallo Holger

    Danke für Deine Antwort. Schade das es nicht geht.

    Wegen dem warum, folgende Überlegung (von Ralf Westphal aufgeschnappt):

    Es interessieren alle Variablen, welche mit einem Pfad zu tun haben
    string meinPfad = ""
    string appPfad = ""
    string userPath = ""

    Wenn das nun alles Datentypen vom Typ path währen, welcher intern auch wieder ein string ist?
    path meinPfad = ""
    path userPath = ""

    Man kann nach einem bestimmten Typ und nicht nach einem unbekannten Namen suchen.

    Warum nicht ein Konstruktor?
    Was ist schöner oder einfacher?

    path meinPfad = ""
    ... = meinPfad

    oder

    path meinPfad = new path("")
    ... = meinPfad.Value

    Es geht, aber Variante 1 währe mir lieber.

    Nochmals Danke und Gruss

    Andreas

     

    Montag, 18. Oktober 2010 13:38
  • Selbstverständlich geht so etwas. Zu diesem Zweck kann man einen impliziten Konvertierungsoperator erzeugen, etwa so:

     public struct Path
     {
       private String path;
    
       public Path(String newPath)
       {
         this.path = newPath;
       }
    
       public static implicit operator Path(String newPath) 
       {
         return new Path(newPath);
       }
     }
    

    Anschließend kann man die Zuweisung genau so durchführen, wie Du das möchtest. Sie verwendet dann den Konstruktor:

    Path p = "test";
    
    Gibt es einen Grund dafür eine Struktur zu verwenden? Hierdurch wird ein Wertetyp erzeugt. Der Vorgang funktioniert zumindest auf die gleiche Art auch mit einer Klasse.

    Montag, 18. Oktober 2010 14:06
  • Hallo Marc

    Funktioniert supper! Vielen Dank!

    Habe gesehen, dass die meisten Typen Strukturen sind und deshalb auch mal eine Struktur verwendet. (String ist eine Klasse) Werde dann je nach Anwendung des neuen Typs entscheiden ob ein Wert- oder Referenztyp besser ist.

    Gruss

    Andreas

     

    Montag, 18. Oktober 2010 14:25
  • Hallo Andreas,

    die verkürzte Schreibweise für Integer (und andere Builtin Types) ist letztendlich semantischer Zuckerguß,
    den der Compiler darüber legt.

    Es im allgemeinen zu erwarten würde viele Probleme verursachen.
    Was Eric Lippert in einer kleinen Artikel-Reihe vor kurzem zu einem ähnlichen "Wunsch" ausgeführt hat:

    Ambiguous Optional Parentheses, Part One
    Ambiguous Optional Parentheses, Part Two
    Ambiguous Optional Parentheses, Part Three

    Im letzten Teil kommt ein prefix unary operator an die Reihe, der hier ins Szenario passt.

    Zu Deinem Beispiel: Gehen täte das mit Bordmitteln.

    using System;
    
    namespace ElmarBoye.Samples.Code
    {
      public struct Pfad
      {
        public Pfad(string value)
        {
          this._value = value;
        }
    
        private string _value;
        public string Value
        {
          get { return this._value ?? ""; }
          set { this._value = value; }
        }
    
        public static implicit operator string(Pfad p)
        {
          return p.Value;
        }
    
        public static implicit operator Pfad(string value)
        {
          return new Pfad(value);
        }
    
        public override string ToString()
        {
          return this.Value;
        }
    
        // TODO: Equals, GetHashCode
    
        
        // Funktionstest
        internal static void TestIt()
        {
          Pfad meinPfad = "1. Pfad";
          Pfad appPfad = meinPfad;
          Pfad userPath = appPfad + ".Noch mehr Pfad";
    
          Console.WriteLine("Mein: {0}, App: {1}, User: {2}", meinPfad, appPfad, userPath);
        }
      }
    }
    
    

    Wobei ich das nun nicht unbedingt im realen Leben täte ;-)

    Funktionieren tut es hier nur, weil wir einen skalaren (String) Wert verpackt haben -
    spätestens mit der zweiten Eigenschaft wäre das Spielchen zu Ende.

    Am Ende ist doch Objekt-Initialisierer der universellere Weg, auch wenn man
    dafür ein, zwei Zeichen (mit Hilfe von Intellisense) mehr tippen muß -
    und der Exkurs bei Eric Lippert beendet werden sollte.

    Gruß Elmar

    Montag, 18. Oktober 2010 14:43
    Beantworter
  • Hallo Elmar

    Vielen Dank für Deinen Hinweis. Werde das noch genauer anschauen.

    Natürlich sehe ich das auch nicht als Lösung für alle Fälle und wie Du schon sagtest, es geht nur bei einer Eigenschaft.

    Gruss

    Andreas

    Montag, 18. Oktober 2010 15:10
  • Hallo Andreas,

    C# ist eine statisch typisierte Sprache. Bei einer Zuweisung muss sichergestellt werden, dass der Laufzeittyp des zugewiesenen Objekts direkt zuweisbar oder zumindest konvertibel ist. Obwohl man einen String implizit parsen und einer Struktur zuweisen kann, bin ich - wie Elmar - der Meinung dass die Einsatzszenarien dafür relativ begrenzt sind.

    using System;
    using System.ComponentModel;
    
    namespace StaticallyTyped
    {
      class Program
      {
        static void Main(string[] args)
        {
          Address a = "Schmoll-Str. 13;79999;Testhausen";
          Console.WriteLine(a.City);
          Console.ReadKey(true);
        }
      }
    
      [TypeConverter(typeof(StringToAddressConverter))]
      struct Address 
      { 
        public string Street {get; set;}
        public string Pob { get; set; }
        public string City { get; set; }
    
        public static implicit operator Address(string s)
        {
          return (Address) TypeDescriptor.GetConverter(typeof(Address)).ConvertFrom(s); 
        }
      }
    
      public class StringToAddressConverter : TypeConverter
      {
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
          return base.CanConvertTo(context, destinationType);
        }
    
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
          if (sourceType == typeof(string))
            return true;
          else
            return base.CanConvertFrom(context, sourceType);
        }
    
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
          if (value is string)
          {
            string stringValue = (string)value;
            string[] parts = stringValue.Split(new char[] { ';' });
    
            Address a = new Address() { Street = parts[0], Pob = parts[1], City = parts[2] };
            return a;
          }
          else
            return base.ConvertFrom(context, culture, value);
        }
      }
    }
    
    

     

    Gruß
    Marcel

    Montag, 18. Oktober 2010 18:23