(Permission is hereby granted by the author for any person or organization to make use of the contents of this post only, for any purpose, including derivative work and for-profit and commercial use, with or without attribution or compensation.)
Properties in C# are powerful and useful enough that they ought to be used, but they are not powerful enough to always reduce code, make implementation simpler and eliminate the possibility of bugs.
Ideally, all properties should implement an interface:
public interface IProperty <TProperty, TParent> { TProperty get (TParent parent); void set (TParent parent, TProperty value); }with the declaration, access and assignment of properties "syntatic sugar" for this interface. For example:
public class MyClass { private int _myField; public int MyProperty { get { return _myField; } set { _myField = value; } } } public void SomeFunction (MyClass myInstance) { if ( myInstance.MyProperty < 0 ) myInstance.MyProperty = 0; }would be an idiom for
public class MyClass { private int _myField; public struct MyProperty_definition : IProperty <int, MyClass> { int get (MyClass parent) { return parent._myField; } void set (MyClass parent, int value) { parent._myField = value; } } public MyProperty_definition MyProperty; } public void SomeFunction (MyClass myInstance) { if ( myInstance.MyProperty.get (myInstance) < 0 ) myInstance.MyProperty.set (myInstance, 0); }This specification allows us to move the backing field into the property, either explicitly:
public class MyClass { public struct MyPropertyType : IProperty <int, MyClass> { private int _backingField; public int get (MyClass parent) { return _backingField; } public void set (MyClass parent, int value) { _backingField = value; } } public MyPropertyType MyProperty; }or implicitly:
public class MyClass { int MyProperty { private int _backingField; public get { return _backingField; } public set { _backingField = value; } } }Making properties use full struct/class semantics would allow us to do nifty stuff like creating meta-properties. For example:
public struct NotifyProperty <TProperty, TParent> : IProperty <TProperty, TParent> where TParent : INotifyPropertyChanged { private TProperty _backingField; private string _propertyName; public NotifyProperty (string name) { _propertyName = name; } public TProperty get (TParent parent) { return _backingField; } public TProperty set (TParent parent, TProperty value) { _backingField = value; if ( parent.PropertyChanged != null ) parent.PropertyChanged (parent, new PropertyChangedEventArgs(_propertyName); } } public class MyClass : INotifyPropertyChanged { public NotifyProperty <int, MyClass> MyIntProperty = new NotifyProperty <int, MyClass> ("MyIntProperty"); public NotifyProperty <string, MyClass> MyIntProperty = ... }Further syntactic sugar might also automatically supply the property name to the instance, although the integration of reflection into C# syntax is not nearly complete.
Finally, auto-implemented properties need to allow the programmer to auto-implement the initialization, using something like an "init new;" element. Instead of this:
public class MyClass { public MyOtherClass MyAutoProperty { get; private set; } public MyClass () { MyAutoProperty = new MyOtherClass (); } }We could have this:
public class MyClass { public MyOtherClass MyAutoProperty { init new; get; private set; } public MyClass () { } }Just to keep things simple, the auto-implemented property would probably have to have a backing field of class type with a default (parameterless) constructor. Perhaps an "init default;" or "init null;" element could also be implemented to explicitly show the auto-implemented property is initialized with the default null value.
You're giving me flashbacks to my previous life... reading up on C# was the last thing I did before I went full-lawyer. Heh.
ReplyDeleteHey! You don't write code, and I won't practice law, OK? :p
ReplyDeleteCould you translate that into Ruby? Thank you very much. :-)
ReplyDeleteSorry, I don't know Ruby.
ReplyDeleteI was having this thought tonight while staring at someone's code, and the mountainous constructors that do nothing more than initialize twenty properties to new, or read in a simple data structure from an entity class. I would have to agree that it would greatly simplify things if one could just specify an init function in the property declaration.
ReplyDeleteBetter to declare a private field and have the property reference it:
ReplyDeleteprivate MyType _field = new MyType ();
public MyType Property { get { return _field; } }
is better than
public MyClass () { Property = new MyType (); }
public MyType Property { get; private set; }