Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Intelligentes PropertySet
syn87
myCSharp.de - Member



Dabei seit:
Beiträge: 87

Themenstarter:

Intelligentes PropertySet

beantworten | zitieren | melden

Gutn Abend zusammen :)

jeder kennt wahrscheinlich eine Methode ähnlich der Folgenden:


void SetProperty(string propertyName, ref int backing, int value)
{ ... }

Ich bin hier mal auf etwas gestoßen, was den MagicString kram umgeht:


 #region INotifyPropertyChanged Members   ...
2
3
4        [field:NonSerialized()]
5        public event PropertyChangedEventHandler PropertyChanged;
6
7        private void OnPropertyChanged(Expression<Func<object>> exp)
8        {
9            MemberExpression me = exp.Body as MemberExpression;
10
11            if (me == null)
12            {
13                me = ((UnaryExpression)exp.Body).Operand as MemberExpression;
14            }
15
16         
17            string propertyName = me.Member.Name;
18            OnPropertyChanged(propertyName);
19
20        }
21        private void OnPropertyChanged(string propName)
22        {
23            if (PropertyChanged != null)
24                PropertyChanged(this, new PropertyChangedEventArgs(propName));
25        }
26        #endregion

Vllt hat ja noch jemand Verbesserungsvorschläge.

LG
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.813
Herkunft: Waidring

beantworten | zitieren | melden

Hallo,

sehr interessanter Ansatz.

Allerdings wäre der Link zum (vermutlichen) Original nicht schlecht:
How to use INotifyPropertyChanged, the type-safe way (no magic string)


mfG Gü


[Edit]
Hab anscheinend
Zitat
Ich bin hier mal auf etwas gestoßen, was den MagicString kram umgeht:
falsch interpretiert.
[/Edit]
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von gfoidl am .

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
syn87
myCSharp.de - Member



Dabei seit:
Beiträge: 87

Themenstarter:

beantworten | zitieren | melden

Das Original entstammt meinerseits von CodeKeep. Demnach auch nicht wirklich eine Referenz wert ;)

LG
private Nachricht | Beiträge des Benutzers
Briefkasten
myCSharp.de - Member

Avatar #avatar-1523.gif


Dabei seit:
Beiträge: 446

beantworten | zitieren | melden

Ich finde die implementierung über Extenions schöner.

http://www.lieser-online.de/blog/?p=130

http://www.lieser-online.de/blog/?p=131
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Briefkasten am .
Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo zusammen,

ich weiß ja, dass Microsoft den Präprozessor aus guten Gründen so stark beschnitten hat, aber das wäre eigentlich ein klassischer Fall für das gute alte __FUNCTION__, dass durch den aktuellen Funktionsnamen ersetzt wird. Müsste es hier natürlich analog für Properties geben.

Das Problem bei den Lösung über Lambda-Audrücke ist doch, dass zwar geprüft wird, ob es den Property-Namen gibt, aber nicht, ob in der Property auch der Name dieser Property verwendet wird. Copy&Paste-Fehler bleiben also weiterhin unentdeckt.

In [Artikel] Attribute zur Prüfung von Properties verwenden verwende ich einen alternativen Weg, um sicherzustellen, dass der aktuelle Name der Property verwendet wird. Auch diese Lösung hat ihr Schönheitsfehler, aber zumindest muss man da den Namen der Property gar nicht mehr tippen, sondern er wird automatisch aus dem StackTrace ermittelt.

herbivore
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.813
Herkunft: Waidring

beantworten | zitieren | melden

Hallo,

am idealsten wäre natürlich wenn der Compiler den Code für OnPropertyChanged genieren würde. Dann wäre das auch mit den "automatic properties" möglich.

Weiters es gäbe es keine Performanceeinbußen durch die Verwendung von Reflektion wenn herbivores alternativer Weg verwendet wird. Dieser der Vollständigkeit halber:


protected void OnPropertyChanged()
{
	MethodBase mb = new StackFrame(1).GetMethod();
	string propertyName = mb.Name.Substring(4);	// set_ auslassen.
	OnPropertyChanged(propertyName);
}

Könnte doch von Mircosoft berücksichtigt werden. Es gibt ja schon so vieles was [CompilerGenerated] ist.


mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo gfoidl,

wobei man dann noch dazusagen sollte, dass man

[MethodImpl (MethodImplOptions.NoInlining)]

vor jede Property schreiben muss, in der man OnPropertyChanged benutzt, weil sonst die Wahrscheinlichkeit hoch ist, dass die Property gar nicht im StackTrace auftaucht und entsprechend auch nicht ausgelesen werden kann.

herbivore
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.813
Herkunft: Waidring

beantworten | zitieren | melden

Hallo herbivore,

berechtigter Einwand.

Wird die Property aber so codiert


private string _name;
public string Name
{
	get { return _name; }
	set
	{
		if (_name == value)
			return;

		_name = value;
		OnPropertyChanged();
	}
}
wird kein Inlining durchgeführt (wegen dem return-Statement). Und somit ist der explizite Hinweis mit dem Attribut nicht unbedingt notwendig.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo gfoidl,

wieso sollte wegen des returns kein Inlining möglich sein? Es ist nicht nur möglich, sondern ich habe genau deinen Code genommen und im Release wird da tatsächlich Inlining verwendet. Mich hätte eher gewundert, wenn nicht.

herbivore
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.813
Herkunft: Waidring

beantworten | zitieren | melden

Hallo herbivore,
Zitat
wieso sollte wegen des returns kein Inlining möglich sein?
Für die Schlussfolgerung gibt es keinen Beweis daher ist sie wohl nicht richtig. Sie kam deshalb zu Stande da die Empirie dies bei mir (Win XP, .net 3.5 SP1, 32 bit) zeigt.

Zum Testen wurde dieser Code verwendet:


using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;

namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Foo foo = new Foo();
			foo.PropertyChanged += new PropertyChangedEventHandler(foo_PropertyChanged);

			foo.Name = "Hallo";
			Console.ReadKey();
		}
		//---------------------------------------------------------------------
		static void foo_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			Console.WriteLine(e.PropertyName);
		}
	}
	//-------------------------------------------------------------------------
	class Foo : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler PropertyChanged;
		//---------------------------------------------------------------------
		protected void OnPropertyChanged()
		{
			MethodBase mb = new StackFrame(1).GetMethod();

			Console.WriteLine(mb);

			string propertyName = mb.Name.Substring(4);	// set_ auslassen.
			OnPropertyChanged(propertyName);
		}
		//---------------------------------------------------------------------
		protected void OnPropertyChanged(string name)
		{
			PropertyChangedEventHandler tmp = PropertyChanged;
			if (tmp != null)
				tmp(this, new PropertyChangedEventArgs(name));
		}
		//---------------------------------------------------------------------
		private string _name;
		public string Name
		{
			get { return _name; }
			set
			{
				if (_name == value)
					return;

				_name = value;
				OnPropertyChanged();
			}
		}
	}
}
Wenn ich das in der Release-Konfiguration laufen lasse lasse kommt auf zwei getesteten Rechner immer das selbe Ergebnis raus:

Mit if (...) return gehts -> wird offensichtlich nicht "Ingelint"
Ohne if (...) -> wird ingelint.

Welchen Rechner hast du oder welche Framework-Version?


Aber mit dem MethodImpl-Attribut ist man auf der sicheres Seite und daher sollte dies verwendet werden.

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo gfoidl,

Windows 2000, 32bit, .NET 2.0

herbivore
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.813
Herkunft: Waidring

beantworten | zitieren | melden

Zitat
Windows 2000
Vernüftig

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
winSharp93
myCSharp.de - Experte

Avatar #avatar-2918.png


Dabei seit:
Beiträge: 5.742
Herkunft: Stuttgart

beantworten | zitieren | melden

Hallo zusammen,

im Vergleich zum Auslesen aus dem StackTrace ist die Methode mit der Expression aber IMHO um einiges eleganter, performanter und sicherer.
Darauf, dass unter gewissen Umständen eventuell das Attribut redundant sein könnte, sollte man sich überhaupt nicht verlassen - ein neuer JIT-Compiler oder ngen können das alles zunichte machen

Ich persönlich wünsche mir aber immer noch etwas wie:


public class Foo
{
   public string Bar
   {
       get;
       set;
   }
}
//...
string propertyName = typeof(Foo.Bar).Name;
wobei das typeof(Foo.Bar) ein PropertyInfo liefert.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von winSharp93 am .
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo winSharp93,

bei der Performance gebe ich dir Recht, bei der Eleganz gibt es zwei Seiten und bei der Sicherheit kann ich deinen Standpunkt nicht nachvollziehen.

Der Performance über den StackTrace ist relativ schlecht. Daraus habe ich nie einen Hehl gemacht. Ob sie bei Properties, die in der Regel auf der Oberfläche angezeigt werden (INotifyPropertyChanged steht ja in enger Beziehung zum DataBinding) zu schlecht ist, glaube ich aber nicht.

Wenn man den Namen gar nicht angeben muss, ist das auf jeden Fall extrem elegant. Besser geht es nicht. Dass der Name dann über den StackTrace ermittelt wird und dabei sogar noch Substring verwendet werden muss, ist natürlich sehr unschön, aber eben Mittel zum Zweck.

Die mögliche Unsicherheit bezüglich neuer, inkompatibler Compiler-Versionen schätze ich als sehr gering ein. Die Unsicherheit, das der Programmierer durch Copy&Paste eine falschen Namen angibt, ist dagegen vollkommen real und wird immer wieder vorkommen. Wenn du diese beiden Aspekte gegeneinander abwägst, ist mir schleierhaft, wie du zu einen anderen Ergebnis kommen kannst.

herbivore
private Nachricht | Beiträge des Benutzers
winSharp93
myCSharp.de - Experte

Avatar #avatar-2918.png


Dabei seit:
Beiträge: 5.742
Herkunft: Stuttgart

beantworten | zitieren | melden

Zitat von herbivore
bei der Sicherheit kann ich deinen Standpunkt nicht nachvollziehen.
"Sicherheit" ist vermutlich das falsche Wort.
Was ich sagen wollte, war die Zuverlässigkeit bzw. die "Sicherheit", dass es auch immer funktioniert.
Zitat von herbivore
Wenn man den Namen gar nicht angeben muss, ist das auf jeden Fall extrem elegant. Besser geht es nicht.
Da stimme ich dir zu.
Zitat von herbivore
Dass der Name dann über den StackTrace ermittelt wird und dabei sogar noch Substring verwendet werden muss, ist natürlich sehr unschön, aber eben Mittel zum Zweck.
Wobei ich mir die Frage stelle, ob es das wirklich wert ist.
Was passiert, wenn man OnPropertyChanged nicht aus einer Property heraus aufruft?
Zum Beispiel aus einer anderen Methode oder aus einer Property, welche die Änderung einer anderen zur Folge hat (weil sie auf dem geänderten Wert basiert).

Ein weiteres Szenario: Durch etwas Refaktoring hat man plötzlich eine Methode OnNameChanged, in der man PropertyChanged aufruft - und dann funktioniert der Code auf einmal nicht mehr...

Eventuell setzt man noch irgendwelche Rewriter ein (Aspektorientierung oder was auch immer), die den Stacktrace ändern.
Zitat von herbivore
Die mögliche Unsicherheit bezüglich neuer, inkompatibler Compiler-Versionen schätze ich als sehr gering ein
Das bezog sich eher auf das Weglassen des expliziten NoInlining.
Zitat von herbivore
Wenn du diese beiden Aspekte gegeneinander abwägst, ist mir schleierhaft, wie du zu einen anderen Ergebnis kommen kannst.
Ein paar Gründe habe ich ja schon genannt, aber um es zu verallgemeinern:
Eine Methode sollte sich nicht in diesem Grade (eigentlich überhaupt nicht) darauf verlassen, wie der Callstack aussieht bzw. wer sie aufgerufen hat.
Außerdem erwartet man dieses Verhalten auch nicht von einer derartigen Methode; sie "bricht" in ihrem Verhalten sonst eher Konvenktionen.
Eine Methode, die statt eines Parameters vom Aufrufer verlangt, an einer ganz bestimmten Stelle aufgerufen zu werden, halte ich für sehr schlecht.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo winSharp93,
Zitat
Was passiert, wenn man OnPropertyChanged nicht aus einer Property heraus aufruft?
ich würde sowieso nicht OnPropertyChanged direkt aufrufen, sondern eine extra Methode schreiben, die den Namen der Property ermittelt, die EventArgs erzeugt, den Namen reinschreibt und damit dann OnPropertyChanged aufruft. Und diese Methode muss dann - per Definition - immer direkt aus einer Property aufgerufen werden. In allen von dir genannten Fällen - dir ich alle für nicht besonderes wahrscheinlich halte -, kann man dann diese Methode eben nicht verwenden, sondern muss OnPropertyChanged selbst mit den passenden EventArgs mit dem passenden Namen aufrufen. Insofern hat man dann weiterhin die volle Kontrolle und die volle Flexibilität, gleichzeitig aber eine große Erleichterung für den Standard-Fall.

herbivore
private Nachricht | Beiträge des Benutzers
winSharp93
myCSharp.de - Experte

Avatar #avatar-2918.png


Dabei seit:
Beiträge: 5.742
Herkunft: Stuttgart

beantworten | zitieren | melden

Zitat von herbivore
sondern eine extra Methode schreiben, die den Namen der Property ermittelt, die EventArgs erzeugt, den Namen reinschreibt und damit dann OnPropertyChanged aufruft.
Also etwas wie:


public int Foo
{
     //...
    set
    {
        this.OnPropertyChanged(Helper.GetCallingPropertyName());
    }
}

Ok - das wäre tatsächlich sauber.
private Nachricht | Beiträge des Benutzers