Laden...

Coding Guidelines Property

Erstellt von DerHulk vor 16 Jahren Letzter Beitrag vor 16 Jahren 2.120 Views
DerHulk Themenstarter:in
270 Beiträge seit 2005
vor 16 Jahren
Coding Guidelines Property

Hallo Forum,

würde gern mal wissen ob man Properties innerhalb einer Klasse den Membervariablen vorzuziehen soll/muss?

Sollte man so implementieren?


        private int m_Foo;

        public int Foo
        {
            get { return m_Foo; }
            set { m_Foo= value; }
        }

        public void Bar()
        {
            //code
            if (m_Foo == 1) TuWas();//mehr code
            //noch mehr code
        }

Oder doch so?


        private int m_Foo;

        public int Foo
        {
            get { return m_Foo; }
            set { m_Foo= value; }
        }

        public void Bar()
        {
            //code
            if (this.Foo == 1) TuWas();//mehr code
            //noch mehr code
        }

Evtl. ist das ja nur eine philosopische Frage?! Vor\Nachteile?!

mfg Hulk

6.862 Beiträge seit 2003
vor 16 Jahren

Bei diesen Trivialproperties wo die Variablen nur gekapselt werden ohne irgendwelche Validierungslogik oder ähnlichen ist es prinzipiell egal. Nur wenn man ganz ganz viele Propertyzugriffe macht, sollte man im Hinterkopf haben das es Funktionsaufrufe sind und damit den Overhead den ein Funktionsaufruf mit sich bringt, einfängt. Und das kann extrem vielen Zugriffen doch langsamer sein als wenn man direkt auf die Variable zugreift. Aber das ist nen Detail was normal fast egal ist. Nur eins sollte man machen. Konsequent bleiben - also entweder nur direkt immer Variablen, oder immer die dazugehörigen Properties verwenden - nicht beides mischen.

PS.: Bei der If Bedingung kommt nen C/C++ Programmierer durch oder? So wie da mitm Datentyp geschlampt wird 😁

Baka wa shinanakya naoranai.

Mein XING Profil.

F
10.010 Beiträge seit 2004
vor 16 Jahren

@DerHulk:
Immer auf die Property zugreifen, ausser es ist der Setter, das macht sonst nen Stackoverflow.

@Talla:
Im Releasemode compiliert die Runtime local, wenn möglich, ist also von der Performance egal.

6.862 Beiträge seit 2003
vor 16 Jahren

@Talla:
Im Releasemode compiliert die Runtime local, wenn möglich, ist also von der Performance egal.

Das ist mir aber neu. Hast du vielleicht nen Link wo das beschrieben ist oder nen Beispielcode wo das provoziert wird? Ich habs nicht nachvollziehen können.

Habs in der Art probiert:

using System;
using System.Diagnostics;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {
            Test t = new Test();
            Stopwatch s = new Stopwatch();
            s.Start();
            t.Variablentest();
            s.Stop();
            Console.WriteLine("Schleife mit Variable: " + s.ElapsedMilliseconds.ToString());
            s.Reset();
            s.Start();
            t.Propertytest();
            s.Stop();
            Console.WriteLine("Schleife mit Property: " + s.ElapsedMilliseconds.ToString());
            Console.ReadLine();
        }
    }

    public class Test {
        const int anz = 100000000;

        int? t = 0;

        public int? T {
            get {
                return t;
            }
            set {
                    t = value;
            }
        }

        public void Variablentest() {
            for (int i = 0; i < anz; i++) {
                t++;
            }
            Console.WriteLine(t);
            t = 0;
        }

        public void Propertytest() {
            for (int i = 0; i < anz; i++) {
                T++;
            }
            Console.WriteLine(T);
            T = 0;
        }
    }
}

Zumindest der IL Code der rauskommt und auch die Zeiten die er mir ausgibt geben keinen Hinweis darauf das das Property wegoptimiert wird, würde mich auch arg wundern wenn er das machen würde.

Baka wa shinanakya naoranai.

Mein XING Profil.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo talla,

ich kenne das eher so, dass er triviale Properties meistens wegoptimiert. Das war ja auch ein Problem in [Artikel] Attribute zur Prüfung von Properties verwenden

herbivore

6.862 Beiträge seit 2003
vor 16 Jahren

Hmm, auch wieder war, das Thema hatten wir damals schon.

Du hast nicht zufällig nen Kompilat noch rumliegen wo das Property wegoptimiert wurde oder? Wenn nicht schau ich nacher mal selber ob ich mit deinem Code das irgendwie hinbekomme. Interessiert mich jetzt schon unter welchen Umständen es wegoptimiert wird.

Baka wa shinanakya naoranai.

Mein XING Profil.

432 Beiträge seit 2005
vor 16 Jahren

hi hulk,

ergänzend zu talla sei noch gesagt, dass der Zugriff auf das Property entscheidend andere Auswirkungen haben kann, wenn selbiges als virtual angelegt und dann von einer erbenden Klasse überschrieben wird.

Um es mal mit Code auszudrücken:


class MyThing
{
   private int _Count;

   public virtual int Count
   {
      get { return _Count; }
      set { _Count = value; }
   }

   protected virtual int GetSomeResult()
   {
      return Count;  // würde den Inhalt der abgeleiteten Variablen liefern, also bei MyDerivedThing: 0
      return _Count; // würde den Inhalt der hier lokal vorgehaltenen Variablen liefern
   }
}

class MyDerivedThing : MyThing
{
   public override int Count
   {
      get { return 0; }
      set { }
   }
}

Da eine abgeleitete Klasse selbst entscheiden sollte, ob sie Funktionalität der Basisklasse verändert, deklarieren wir bspw. alle Methoden und Properties grundsätzlich als virtuell.

Daher haben wir es uns zur Gewohnheit gemacht, auch in der Klasse selbst nur grundsätzlich auch nur die Properties anzusprechen.

hth
ron

S
8.746 Beiträge seit 2005
vor 16 Jahren

hi hulk,
Da eine abgeleitete Klasse selbst entscheiden sollte, ob sie Funktionalität der Basisklasse verändert, deklarieren wir bspw. alle Methoden und Properties grundsätzlich als virtuell.

Why is non-virtual the default in C#?

There are several reasons. One is performance. We can observe that as people write code in Java, they forget to mark their methods final. Therefore, those methods are virtual. Because they're virtual, they don't perform as well. There's just performance overhead associated with being a virtual method. That's one issue.

A more important issue is versioning. There are two schools of thought about virtual methods. The academic school of thought says, "Everything should be virtual, because I might want to override it someday." The pragmatic school of thought, which comes from building real applications that run in the real world, says, "We've got to be real careful about what we make virtual."

When we make something virtual in a platform, we're making an awful lot of promises about how it evolves in the future. For a non-virtual method, we promise that when you call this method, x and y will happen. When we publish a virtual method in an API, we not only promise that when you call this method, x and y will happen. We also promise that when you override this method, we will call it in this particular sequence with regard to these other ones and the state will be in this and that invariant.

Every time you say virtual in an API, you are creating a call back hook. As an OS or API framework designer, you've got to be real careful about that. You don't want users overriding and hooking at any arbitrary point in an API, because you cannot necessarily make those promises. And people may not fully understand the promises they are making when they make something virtual.

Dazu kommt das "fragile base class problem".

Java 5 adressiert beide Probleme mit der @Override-Annotation. Dann kriegt man wenigstens mit, wenn sie die Base Class geändert hat. Der "Vorteil" bei Java 5+ besteht nun darin, dass man sich warnen lassen kann, oder eben auch nicht. Aus Sicht eines Contract-Driven-Design ist diese Freiheit natürlich kritisch zu sehen. Aber Java hatte auch keine andere Wahl.

DerHulk Themenstarter:in
270 Beiträge seit 2005
vor 16 Jahren

Hallo,danke für die Antworten.

Also gibt es dabei Drei Probleme :
1.Performance 1.Unerwartetes Verhalten (weil z.B. über die Vererbung sich was geändert haben könnte) 1.Und die Konsitenz des Codes (man will ja möglichst immer das Gleiche verwenden)

Wenn ich die Performace vernachlässige habe ich die anderen zwei abgedeckt.
Jedoch sind 80% meiner Props trivial. 🤔

Was dann doch für Situations abhäniges Handel spricht oder?

mfg Hulk

@Talla Sorry leider kein c/c++ Know How!

432 Beiträge seit 2005
vor 16 Jahren

@svenson,

hi und danke für diese information.
aber gilt der performance verlust auch für c#? denn der text bezieht sich ja auf java.

6.862 Beiträge seit 2003
vor 16 Jahren

Darum ging es Svenson ja grad, zu zeigen warum in C# die Methoden nicht per default virtual sind. Wenn ihr alle virtual macht, habt ihr natürlich die gleichen "Probleme" wie Java.

Baka wa shinanakya naoranai.

Mein XING Profil.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo citizen.ron,

der Performace-Unterschied bezieht sich unabhängig von Java und C# einfach auf den Vergleich von virtuellen zu nicht virtuellen Methoden und Properties.

Dieser Performance-Unterschied ist im Prinzip sogar zu vernachlässigen, denn er liegt nur bei ca. 10% bezogen auf einen einfachen Methodenaufruf. Allerdings verhindert virtual leider Inlining. Und damit kann der Unterschied auch deutlich größer als die genannten 10% ausfallen. Natürlich immer noch bezogen, auf einen einfachen Methodeaufruf und nur bei Methoden/Properties, für die tatsächlich Inlining in Frage kommt. Da aber Programme mehr machen, als Methoden aufzurufen, wird der Performance-Unterschied bei realen Programmen i.d.R. nicht spürbar sein.

herbivore

432 Beiträge seit 2005
vor 16 Jahren

@herbivore,
danke für deine Meinung.

Ich würde die Diskussion gerne nochmal auf den ursprünglichen Anlass zurücklenken, wann man Properties oder Membervariablen verwendet.

Gegeben sei eine einfache Klasse, die in der Lage ist, ihren Zustand über ein Property "State" zu beschreiben oder verändern zu lassen:


namespace PropertyDiscussion
{
   [Flags]
   public enum State
   {
      ReadOnly = 0,
      CanEdit  = 1, 
      Editing  = 2,
      CanSave  = 4
   }

   class Subview
   {
      private State _State;
      public virtual State _State
      {
         get { return _State; }
         set { _State = value; }
      }
   }
}

Ein ganz typisches Muster (wahrscheinlich nicht nur) bei uns ist nun, dass ausserhalb der Klasse jemand erfahren möchte, dass sich dieser Zustand ändern wird oder geändert hat. Ersteres wird dabei so ausgelegt, dass ggf. die Zustandsänderung verhindert werden kann.

Dazu braucht man Ereignisse und konsequenterweise auch die Ereignisauslöser und der Setter des Property wird entsprechend geändert.

Dann landet man bei folgender Mindestimplementierung:


namespace PropertyDiscussion
{
   [Flags]
   public enum ViewState
   {
      ReadOnly = 0,
      CanEdit  = 1, 
      Editing  = 2,
      CanSave  = 4
   }

   public class ViewStateArgs
   {
      public bool Cancel;
      public ViewState OldState;
      public ViewState NewState;

      public ViewStateArgs(ViewState oldState, ViewState newState)
      {
         OldState = oldState;
         NewState = newState;
         Cancel = false;
      }
   }

   public delegate void ViewStateChangeEventHandler(object sender, ViewStateArgs e);

   public class Subview
   {
   #region Members
      private ViewState    _State = ViewState.CanEdit;
   #endregion

   #region Properties
      public virtual ViewState _State
      {
         get { return _State; }
         set 
         {
            if (_State != value)
            {
               ViewStateArgs e = new ViewStateArgs(_State, value);
               OnStateChanging(e);
               if (!e.Cancel)
               {
                  _State = value;
                  OnStateChanged();
               }
            }
         }
      }
   #endregion

   #region Events
   // Ereignisse
      public event ViewStateChangeEventHandler  StateChanging;
      public event EventHandler                 StateChanged;

   // Ereignisauslöser
      protected virtual void OnStateChanging(ViewStateArgs e)
      {
         if (StateChanging != null)
            StateChanging(this, e);
      }
      protected virtual void OnStateChanged()
      {
         if (StateChanged != null)
            StateChanged(this, EventArgs.Empty);
      }
   #endregion
   }
}

Folgende drei Fragen möchte ich nun zur Diskussion stellen: 1.Wenn die (Basis-)Klasse verhindern möchte, dass trotz Zustandsänderung die Ereignisse gefeuert werden, könnte sie statt der Zuweisung State = ... auch die Membervariable benutzen. Ist das eine legitime Vorgehensweise? 1.Wollte eine abgeleitete Klasse ebenfalls die Ereignisse umgehen, könnte die Membervariable als protected deklariert werden oder die Ereignisauslöser überschrieben werden oder der Setter überschrieben werden. Welches hiervon ist die beste Vorgehensweise? 1.Sollten Zustandsänderungen, die mit Ereignissen verknüpft sind, überhaupt die Ereignisse umgehen können?

Danke für Eure Meinungen.

Gruß
ron

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo citizen.ron,

zu 1. ja, man stelle sich vor, dass man den Zustand temporär ändert, aber vor Ende der Operation wiederherstellt. Dann hat sich aus Sicht des Benutzers der Zustand gar nicht geändert. Entsprechend sollte der Event nicht ausgelöst werden.

Andersherum sollte der Event aber immer gefeuert werden, wenn der Zustand sich aus Sicht des Benutzers tatsächlich ändert.

zu 2. ja, grundsätzlich können Felder protected gemacht werden. Es liegt natürlich eine gewisse Gefahr darin. Entsprechend muss man abwägen, ob man es tut. Sicher hängt die Entscheidung auch davon ab, wie eng Ober- und Unterklasse verwoben sind bzw. sein sollen.

zu 3. Ja, aus dem in 1. genannten Grund.

herbivore