Best way to implement generic setting class - Get/Set Properties reflected? - c#

I don't know how I can make a generic settings class and hope that you can help me.
First of all I want a single settings file solution. For this I have created a Singleton like this:
public sealed class Settings
{
private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();
public void Load(string fileName)
{
throw new NotImplementedException();
}
public void Save(string fileName)
{
throw new NotImplementedException();
}
public void Update()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public string GetPropery(string propertyName)
{
return m_lProperties[propertyName].ToString() ?? String.Empty;
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns></returns>
public string GetPropery(string propertyName, string defaultValue)
{
if (m_lProperties.ContainsKey(propertyName))
{
return m_lProperties[propertyName].ToString();
}
else
{
SetProperty(propertyName, defaultValue);
return defaultValue;
}
}
/// <summary>
/// Sets the property.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="value">The value.</param>
public void SetProperty(string propertyName, string value)
{
if (m_lProperties.ContainsKey(propertyName))
m_lProperties[propertyName] = value;
else
m_lProperties.Add(propertyName, value);
}
}
But I think the better way is that the properties are in the classes and I can get the properties through reflection.
- Can you help me to implement something like this?
- Is it possible to give properties attributes like "encrypted = true"?
- Whats the best way to save / load the settings in a xml file?
Updated
Here is a example how to use the settings actual:
class Test()
{
private string applicationPath;
private string configurationPath;
private string configurationFile;
public Test()
{
applicationPath = Settings.Instance.GetPropery("ApplicationPath", AppDomain.CurrentDomain.BaseDirectory);
configurationPath = Settings.Instance.GetPropery("ConfigurationPath", "configurations");
configurationFile = Settings.Instance.GetPropery("ConfigurationFile", "application.xml");
// ... Load file with all settings from all classes
}

This here is a rather relevant bit from my own code.
public class MyObject
{
public string StringProperty {get; set;}
public int IntProperty {get; set;}
public object this[string PropertyName]
{
get
{
return GetType().GetProperty(PropertyName).GetGetMethod().Invoke(this, null);
}
set
{
GetType().GetProperty(PropertyName).GetSetMethod().Invoke(this, new object[] {value});
}
}
}
what it allows, is this:
MyObject X = new MyObject();
//Set
X["StringProperty"] = "The Answer Is: ";
X["IntProperty"] = 42;
//Get - Please note that object is the return type, so casting is required
int thingy1 = Convert.ToInt32(X["IntProperty"]);
string thingy2 = X["StringProperty"].ToString();
Updated: More Explanation
The way this works is to reflectively access properties, properties are different from fields in that they use getters and setters, as opposed to being directly declared and accessed. You can use this same method to get fields, or to also get fields, if you null check the return from GetProperty instead of simply assuming it works. Also, as was pointed out in another comment, this will break if you call it as is with a property that doesn't exist, because it lacks any form of error catching. I showed the code in its simplest possible form, not its most robust form.
As far as property attributes....that indexer needs to be created inside the class you want to use it with (or a parent class, I have it on my BaseObject), so internally you can implement attributes on given properties and then apply switches or checks against the properties when they are accessed. Maybe make all the properties some other custom class where you implement Object Value; Bool Encrypted; then work on it as needed from there, it really just depends on how fancy you want to get and how much code you want to write.

I not reccommend use Reflection in places where it possible do without it, as it very slow.
My example without reflection and Encryption prototype:
public sealed class Settings
{
private static readonly HashSet<string> _propertiesForEncrypt = new HashSet<string>(new string[] { "StringProperty", "Password" });
private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();
public void Load(string fileName)
{
// TODO: When you deserialize property which contains into "_propertiesForEncrypt" than Decrypt this property.
throw new NotImplementedException();
}
public void Save(string fileName)
{
// TODO: When you serialize property which contains into "_propertiesForEncrypt" than Encrypt this property.
throw new NotImplementedException();
}
public void Update()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public object GetPropery(string propertyName)
{
if (m_lProperties.ContainsKey(propertyName))
return m_lProperties[propertyName];
return null;
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns></returns>
public object GetPropery(string propertyName, object defaultValue)
{
if (m_lProperties.ContainsKey(propertyName))
{
return m_lProperties[propertyName].ToString();
}
else
{
SetProperty(propertyName, defaultValue);
return defaultValue;
}
}
/// <summary>
/// Sets the property.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="value">The value.</param>
public void SetProperty(string propertyName, object value)
{
if (m_lProperties.ContainsKey(propertyName))
m_lProperties[propertyName] = value;
else
m_lProperties.Add(propertyName, value);
}
// Sample of string property
public string StringProperty
{
get
{
return GetPropery("StringProperty") as string;
}
set
{
SetProperty("StringProperty", value);
}
}
// Sample of int property
public int IntProperty
{
get
{
object intValue = GetPropery("IntProperty");
if (intValue == null)
return 0; // Default value for this property.
return (int)intValue;
}
set
{
SetProperty("IntProperty", value);
}
}
}

Use a dynamic class like this:https://gist.github.com/3914644 so you could access your properties as: yourObject.stringProperty or yourObject.intProperty

One of the biggest issues is that there is no clean way to de-serialize an Object into an Object. if you dont know ahead of time what the Type of the object needs to be, its very hard to work with. So we have an alternate solution, store the type information.
Given that its not listed, I will provide what I consider an example XML, as well as a method of using it, and a method of accessing the properties themselves. The functions you are using for Get and Set properties are functional as is, and require no changes.
In the individual classes, you need to make sure the relevant properties in that class reference the Settings class in their own get/set methods
public int? MyClassProperty
{
get
{
return (int?)Settings.Instance.GetProperty("MyClassProperty");
}
set
{
Settings.Instance.SetProperty("MyClassProperty", value);
}
}
In your load and save functions, you will want to use Serialization, specifically, XmlSerializer. To do this, you need to declare your list of settings appropriately. For this I would actually use a custom class.
Updated to allow proper loading
public class AppSetting
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlAttribute("pType")]
public string pType{ get; set; }
[XmlIgnore()]
public object Value{ get; set; }
[XmlText()]
public string AttributeValue
{
get { return Value.ToString(); }
set {
//this is where you have to have a MESSY type switch
switch(pType)
{ case "System.String": Value = value; break;
//not showing the whole thing, you get the idea
}
}
}
Then, instead of just a dictionary, you would have something like:
public sealed class Settings
{
private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();
private List<AppSetting> mySettings = new List<AppSetting>();
your load function would be a simple de-serialize
public void Load(string fileName)
{//Note: the assumption is that the app settings XML will be defined BEFORE this is called, and be under the same name every time.
XmlSerializer ser = new XmlSerializer(typeof(List<AppSetting>));
FileStream fs = File.Open(fileName);
StreamReader sr = new StreamReader(fs);
mySettings = (List<AppSetting>)ser.DeSerialize(sr);
sr.Close();
fs.Close();
//skipping the foreach loop that will add all the properties to the dictionary
}
the save function would essentially need to reverse it.
public void Save(string fileName)
{
//skipping the foreach loop that re-builds the List from the Dictionary
//Note: make sure when each AppSetting is created, you also set the pType field...use Value.GetType().ToString()
XmlSerializer ser = new XmlSerializer(typeof(List<AppSetting>));
FileStream fs = File.Open(fileName, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
//get rid of those pesky default namespaces
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
ser.Serialize(sw, mySettings, ns);
sw.Flush();
sw.Close();
fs.Close();
mySettings = null;//no need to keep it around
}
and the xml would resemble something like this:
updated
<ArrayOfAppSetting>
<AppSetting Name="ApplicationPath" pType="System.String">C:\Users\ME\Documents\Visual Studio 2010\Projects\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\</AppSetting>
<AppSetting Name="ConfigurationPath" pType="System.String">configurations</AppSetting>
<AppSetting Name="ConfigurationFile" pType="System.String">application.xml</AppSetting>
<AppSetting Name="prop" pType="System.Int32">1</AppSetting>
</ArrayOfAppSetting>
I showed this example using the intermediate List<> because as it turns out you can't use anything that implements IDictionary with XmlSerializer. It will fail to initialize, it just doesn't work.
You can either create and maintain the list alongside the dictionary, or you can replace the dictionary with the List...make sure you have checks to verify that "Name" is unique, or you can simply ignore the list except during the Save and Load operations (which is how I wrote this example)
Update
This really only works well with Primitive types (int, double, string, etc..), but because you directly store the type, you can use any custom type you want, because you know what it is and what to do with it, you just have to handle it in the set method of AttributeValue
Another Update: If you are only storing strings, instead of objects of all types...it gets ridiculously simpler. get rid of the XmlIgnore value AND the pType, then auto-implement AttributeValue. Boom, done. That will limit you to strings and other primitives though, make sure that the Get/Set for the values in other classes cast them appropriately...but it is a much simpler and easier implementation.

Related

Type.GetConstructor returns null on Iphone

I am developing an app in Xamarin for Iphone, android version of similar app is ready for Google play. I am using jabber-net library in my app for chat functionality. But having some issue on device(Iphone 5 - IOS 7.0.3). this issue doesn't occur in emulator following is the method code.
public class QnameType
{
/// <summary>
/// Element name
/// </summary>
protected internal string Name;
/// <summary>
/// Element namespace URI
/// </summary>
protected internal string NS;
/// <summary>
/// Type to create for NS/Name pair
/// </summary>
protected internal Type ElementType;
/// <summary>
/// Create a QnameType
/// </summary>
/// <param name="name"></param>
/// <param name="ns"></param>
/// <param name="typ"></param>
public QnameType(string name, string ns, Type typ)
{
this.Name = name;
this.NS = ns;
this.ElementType = typ;
}
/// <summary>
/// Is this the same qname by element name and namespace?
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj == (object)this)
return true;
QnameType other = obj as QnameType;
if (other == null)
return false;
return (other.Name == Name) && (other.NS == NS);
}
/// <summary>
/// Get a hash over the name and namespace.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return ToString().GetHashCode();
}
/// <summary>
/// Namespace|Name
/// </summary>
/// <returns></returns>
public override string ToString()
{
return NS + "|" + Name;
}
}
public interface IPacketTypes
{
/// <summary>
/// QName to type mappings.
/// </summary>
QnameType[] Types { get; }
}
public class ElementFactory
{
private Hashtable m_types = new Hashtable();
private static readonly Type[] s_constructorTypes =
new Type[] { typeof(string),
typeof(XmlQualifiedName),
typeof(XmlDocument) };
public void AddType(IPacketTypes list)
{
foreach (QnameType qn in list.Types)
{
this.AddType(qn.Name, qn.NS, qn.ElementType);
}
}
public void AddType(string localName, string ns, Type t)
{
Debug.Assert(t.IsSubclassOf(typeof(Element)));
ConstructorInfo ci = t.GetConstructor(s_constructorTypes);
Debug.Assert(ci != null);
AddType(new XmlQualifiedName(localName, ns), ci);
}
public Element GetElement(string prefix, XmlQualifiedName qname, XmlDocument doc)
{
ConstructorInfo ci = (ConstructorInfo) m_types[qname];
if (ci == null)
{
return new Element(prefix, qname, doc);
}
return (Element) ci.Invoke
(new object[] {prefix, qname, doc});
}
/// <summary>
/// Get a constructor for the appropriate type for the given qname.
/// </summary>
public ConstructorInfo this[XmlQualifiedName qname]
{
get { return (ConstructorInfo) m_types[qname]; }
}
}
t.GetConstructor() returns null on Iphone but works fine on simulator.
Edit: added more detail,
Any help will be highly appreciated.
Thanks
That's likely normal, depending on t itself - what type does it represent ?
By default the managed linker is disabled (Don't link) on the simulator builds. That means every type will be part of the application.
However the default for device builds is Link SDK. This means unused types (found using static analysis) are removed from the application. This allow reducing the size of the application (by not compiling/shipping the whole .NET BCL inside each app).
Static analysis cannot find detect dynamic use of code, e.g. reflection. If your application depends on reflection is needs to preserve the code: using the [Preserve] attribute, an XML file or adding some extra code that will give an hint to the linker to keep the required members.
See documentation for further details.
In addition to the answer from poupou:
If you're stumbling over this question because you have a similar issue with Unity3D then take a look onto this page: https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html
In general that page shows you how to make sure everything is included in IL2CPP builds:
Use [Preserve] keyword on you classes
or
Add a link.xml file into your Assets root

Error Serializing Observable Collection

I have an observable collection I am trying to serialize to disk. The error that is received is :
Type 'VisuallySpeaking.Data.GrammarList' with data contract name
'GrammarList:http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to
DataContractSerializer."} System.Exception
{System.Runtime.Serialization.SerializationException}
Here is my data object:
namespace VisuallySpeaking.Data
{
[CollectionDataContract]
public class GrammarList : ObservableCollection<GrammarDataObject>
{
public GrammarList() : base()
{
Add(new GrammarDataObject("My Name", "My name is","Assets/SampleAssets/MyName.png"));
Add(new GrammarDataObject("Where is", "Where is",""));
Add(new GrammarDataObject("Dog", "I have a dog","/Assets/SampleAssets/westie.jpg"));
}
}
[DataContract]
public class GrammarDataObject : VisuallySpeaking.Common.BindableBase
{
private string _Name;
private string _SpeakingText;
private string _ImagePath;
public GrammarDataObject(string Name, string SpeakingText, string ImagePath)
{
this.Name = Name;
this.SpeakingText = SpeakingText;
this.ImagePath = ImagePath;
}
[DataMember]
public string Name
{
get { return _Name; }
set
{
if (this._Name != value)
{
this._Name = value;
this.OnPropertyChanged("Name");
}
}
}
[DataMember]
public string SpeakingText
{
get { return _SpeakingText; }
set
{
if (this._SpeakingText != value)
{
this._SpeakingText = value;
this.OnPropertyChanged("SpeakingText");
}
}
}
[DataMember]
public string ImagePath
{
get { return _ImagePath; }
set
{
if (this._ImagePath != value)
{
this._ImagePath = value;
this.OnPropertyChanged("ImagePath");
}
}
}
}
Based on Fresh's comments, I have added BindableBase in here as well.
namespace VisuallySpeaking.Common
{
/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
/// </summary>
[Windows.Foundation.Metadata.WebHostHidden]
[DataContract(IsReference = true)]
public abstract class BindableBase : INotifyPropertyChanged
{
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I assume that I have somehow marked my GrammarList class incorrectly, but it escapes me as to how to resolve.
UPDATE:
Following the error message (of course), I added the KnowTypeAttribute and it appeared to work:
[CollectionDataContract(Name = "GrammarList"),KnownType(typeof(GrammarList))]
public class GrammarList : ObservableCollection<GrammarDataObject>
Again, thanks to Fresh, I updated the CollectionDataContract with the Name="GrammarList", but now the issue comes when I rehydrate the XML file from disk. I get the following error message:
Expecting element 'GrammarList' from namespace
'http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data'..
Encountered 'Element' with name 'GrammarDataObject', namespace
'http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data'.
The serialized XML looks like this:
<?xml version="1.0"?>
<GrammarDataObject xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/VisuallySpeaking.Data" i:type="GrammarList">
<GrammarDataObject xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i1">
<ImagePath>Assets/SampleAssets/MyName.png</ImagePath>
<Name>My Name</Name>
<SpeakingText>My name is</SpeakingText>
</GrammarDataObject>
<GrammarDataObject xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i3">
<ImagePath/>
<Name>Where is</Name>
<SpeakingText>Where is</SpeakingText>
</GrammarDataObject>
<GrammarDataObject xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i4">
<ImagePath>/Assets/SampleAssets/westie.jpg</ImagePath>
<Name>Dog</Name>
<SpeakingText>I have a dog</SpeakingText>
</GrammarDataObject>
</GrammarDataObject>
Why is the XML outer tags not listed as "GrammarList"? I would assume that is what the deserializer is looking for. When I manually edit the serialized xml to place GrammarList as the outside tags, it deserializes appropriately. I feel sure I am missing something again!
UPDATE AGAIN
When I was serializing I had the following code:
DataContractSerializer serializer = new DataContractSerializer(typeof(GrammarDataObject));
I changed it to serialize to GrammarList and presto, fixed!!! thanks for the help Fresh.
DataContractSerializer serializer = new DataContractSerializer(typeof(GrammarList));
Looking at the XML output, it looks like the name of the collection is lost when its de-serialized.
Try setting the Name property on the CollectionDataContract i.e.
[CollectionDataContract(Name="GrammarList"),KnownType(typeof(GrammarList))]
public class GrammarList : ObservableCollection<GrammarDataObject>

Better way to trigger OnPropertyChanged

We have a WPF Project that follows the MVVM pattern.
In the View Model there is a lot of code that looks like this:
private string m_Fieldname;
public string Fieldname
{
get { return m_Fieldname; }
set
{
m_Fieldname = value;
OnPropertyChanged("Fieldname");
}
}
Is there a way to do this that would require less code?
Would be nice with something like this:
[NotifyWhenChanged]
public string Fieldname { get; set ; }
You could have a look at PostSharp. They even have a sample at Data Binding. The code taken from there:
/// <summary>
/// Aspect that, when apply on a class, fully implements the interface
/// <see cref="INotifyPropertyChanged"/> into that class, and overrides all properties to
/// that they raise the event <see cref="INotifyPropertyChanged.PropertyChanged"/>.
/// </summary>
[Serializable]
[IntroduceInterface( typeof(INotifyPropertyChanged),
OverrideAction = InterfaceOverrideAction.Ignore )]
[MulticastAttributeUsage( MulticastTargets.Class,
Inheritance = MulticastInheritance.Strict )]
public sealed class NotifyPropertyChangedAttribute : InstanceLevelAspect,
INotifyPropertyChanged
{
/// <summary>
/// Field bound at runtime to a delegate of the method <c>OnPropertyChanged</c>.
/// </summary>
[ImportMember( "OnPropertyChanged", IsRequired = false)]
public Action<string> OnPropertyChangedMethod;
/// <summary>
/// Method introduced in the target type (unless it is already present);
/// raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[IntroduceMember( Visibility = Visibility.Family, IsVirtual = true,
OverrideAction = MemberOverrideAction.Ignore )]
public void OnPropertyChanged( string propertyName )
{
if ( this.PropertyChanged != null )
{
this.PropertyChanged( this.Instance,
new PropertyChangedEventArgs( propertyName ) );
}
}
/// <summary>
/// Event introduced in the target type (unless it is already present);
/// raised whenever a property has changed.
/// </summary>
[IntroduceMember( OverrideAction = MemberOverrideAction.Ignore )]
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Method intercepting any call to a property setter.
/// </summary>
/// <param name="args">Aspect arguments.</param>
[OnLocationSetValueAdvice,
MulticastPointcut( Targets = MulticastTargets.Property,
Attributes = MulticastAttributes.Instance)]
public void OnPropertySet( LocationInterceptionArgs args )
{
// Don't go further if the new value is equal to the old one.
// (Possibly use object.Equals here).
if ( args.Value == args.GetCurrentValue() ) return;
// Actually sets the value.
args.ProceedSetValue();
// Invoke method OnPropertyChanged (our, the base one, or the overridden one).
this.OnPropertyChangedMethod.Invoke( args.Location.Name );
}
}
Usage is then as simple as this:
[NotifyPropertyChanged]
public class Shape
{
public double X { get; set; }
public double Y { get; set; }
}
Examples taken from PostSharp site and inserted for completing the answer
It looks like as if the Framework 4.5 slightly simplifies this:
private string m_Fieldname;
public string Fieldname
{
get { return m_Fieldname; }
set
{
m_Fieldname = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
// ... do stuff here ...
}
This doesn't quite automate things to the extent you're looking for, but using the CallerMemberNameAttribute makes passing the property name as a string unnecessary.
If you're working on Framework 4.0 with KB2468871 installed, you can install the Microsoft BCL Compatibility Pack via nuget, which also provides this attribute.
Josh Smith has a good article on using DynamicObject to do this here
Basically it involves inheriting from DynamicObject and then hooking into TrySetMember. CLR 4.0 only, unfortunately, although it may also be possible using ContextBoundObject in earlier versions but that would probably hurt performance, being primarily suited for remoting\WCF.
IMHO, the PostSharp approach, as in the accepted answer, is very nice and is of course the direct answer to the question asked.
However, for those who can't or won't use a tool like PostSharp to extend the C# language syntax, one can get most of the benefit of avoiding code repetition with a base class that implements INotifyPropertyChanged. There are many examples lying around, but none have so far been included in this useful and well-trafficked question, so here is the version I generally use:
/// <summary>
/// Base class for classes that need to implement <see cref="INotifyPropertyChanged"/>
/// </summary>
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
/// <summary>
/// Raised when a property value changes
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Updates a field for a named property
/// </summary>
/// <typeparam name="T">The type of the field</typeparam>
/// <param name="field">The field itself, passed by-reference</param>
/// <param name="newValue">The new value for the field</param>
/// <param name="onChangedCallback">A delegate to be called if the field value has changed. The old value of the field is passed to the delegate.</param>
/// <param name="propertyName">The name of the associated property</param>
protected void UpdatePropertyField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
OnPropertyChanged(propertyName);
}
/// <summary>
/// Raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">The name of the property that has been changed</param>
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Used, for example, like this:
private int _value;
public int Value
{
get { return _value; }
set { UpdatePropertyField(ref _value, value); }
}
Not quite as concise as being able to just apply a code attribute to an auto-implemented property as in the PostSharp approach, but still goes a long way to speeding the implementation of view models and other similar types.
The key features above that distinguish it from some other implementations:
Equality is compared using EqualityComparer<T>.Default. This ensures that value types can be compared without being boxed (a common alternative would be object.Equals(object, object)). The IEqualityComparer<T> instance is cached, so after the first comparison for any given type T, it's very efficient.
The OnPropertyChanged() method is virtual. This allows derived types to easily and efficiently handle property changed events in a centralized way, without having to subscribe to the PropertyChanged event itself (e.g. for multiple levels of inheritance) and also of course gives the derived type better control over how and when it handles the property changed event relative to raising the actual PropertyChanged event.
Ok this doesn't clean the code up but it shortens the amount of time writing all of this code. I can now blow through a list of 20+ properties in a couple of minutes.
First you need to define all of your private variables, I'm assuming your first character is a lower case. Now copy that those variables into another list as the macro removes the original line.
For example:
private int something1 = 0;
private int something2 = 0;
private int something3 = 0;
private int something4 = 0;
private int something5 = 0;
private int something6 = 0;
Then put your cursor somewhere on that line and run this macro. Again this replaces the line with a public property so make sure you have the same private member variable defined before this in your class.
I'm sure this script could be cleaned up, but it saved me hours of tedious work today.
Sub TemporaryMacro()
DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstText)
DTE.ActiveDocument.Selection.Delete(7)
DTE.ActiveDocument.Selection.Text = "public"
DTE.ActiveDocument.Selection.CharRight()
DTE.ExecuteCommand("Edit.Find")
DTE.Find.FindWhat = " "
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
DTE.ActiveDocument.Selection.CharRight()
DTE.ActiveDocument.Selection.WordRight(True)
DTE.ActiveDocument.Selection.CharLeft(True)
DTE.ActiveDocument.Selection.Copy()
DTE.ActiveDocument.Selection.CharLeft()
DTE.ActiveDocument.Selection.CharRight(True)
DTE.ActiveDocument.Selection.ChangeCase(VsCaseOptions.VsCaseOptionsUppercase)
DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstText)
DTE.ExecuteCommand("Edit.Find")
DTE.Find.FindWhat = " = "
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
DTE.ActiveDocument.Selection.CharLeft()
DTE.ActiveDocument.Selection.EndOfLine(True)
DTE.ActiveDocument.Selection.Delete()
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "get { return "
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = "; }"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "set"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "if("
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = " != value)"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = " = value;"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "OnPropertyChanged("""
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = """);"
DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstText)
DTE.ExecuteCommand("Edit.Find")
DTE.Find.FindWhat = """"
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
DTE.ActiveDocument.Selection.CharRight()
DTE.ActiveDocument.Selection.CharRight(True)
DTE.ActiveDocument.Selection.ChangeCase(VsCaseOptions.VsCaseOptionsUppercase)
DTE.ActiveDocument.Selection.Collapse()
DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.LineDown()
DTE.ActiveDocument.Selection.EndOfLine()
End Sub
I'd use PropertyChanged.Fody NuGet package. It's as simple as:
[PropertyChanged.ImplementPropertyChanged]
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
All public properties will raise PropertyChanged event under the hood.
P.S. Syntax changed in newer versions. This example is for version 1.52.1

How to document a class property

What is the correct syntax for an XML comment for a class property?
In response to a request for explicit examples, the following extract is from StyleCop help document, "SA1623: PropertySummaryDocumentationMustMatchAccessors":
The property’s summary text must begin with wording describing the types of accessors exposed within the property. If the property contains only a get accessor, the summary must begin with the word “Gets”. If the property contains only a set accessor, the summary must begin with the word “Sets”. If the property exposes both a get and set accessor, the summary text must begin with “Gets or sets”.
For example, consider the following property, which exposes both a get and set accessor. The summary text begins with the words “Gets or sets”.
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
set { this.name = value; }
}
If the property returns a Boolean value, an additional rule is applied. The summary text for Boolean properties must contain the words “Gets a value indicating whether”, “Sets a value indicating whether”, or “Gets or sets a value indicating whether”. For example, consider the following Boolean property, which only exposes a get accessor:
/// <summary>
/// Gets a value indicating whether the item is enabled.
/// </summary>
public bool Enabled
{
get { return this.enabled; }
}
In some situations, the set accessor for a property can have more restricted access than the get accessor. For example:
/// <summary>
/// Gets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
private set { this.name = value; }
}
In this example, the set accessor has been given private access, meaning that it can only be accessed by local members of the class in which it is contained. The get accessor, however, inherits its access from the parent property, thus it can be accessed by any caller, since the property has public access.
In this case, the documentation summary text should avoid referring to the set accessor, since it is not visible to external callers.
StyleCop applies a series of rules to determine when the set accessor should be referenced in the property’s summary documentation. In general, these rules require the set accessor to be referenced whenever it is visible to the same set of callers as the get accessor, or whenever it is visible to external classes or inheriting classes.
The specific rules for determining whether to include the set accessor in the property’s summary documentation are:
1.The set accessor has the same access level as the get accessor. For example:
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
protected string Name
{
get { return this.name; }
set { this.name = value; }
}
2.The property is only internally accessible within the assembly, and the set accessor also has internal access. For example:
internal class Class1
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
protected string Name
{
get { return this.name; }
internal set { this.name = value; }
}
}
internal class Class1
{
public class Class2
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
internal set { this.name = value; }
}
}
}
3.The property is private or is contained beneath a private class, and the set accessor has any access modifier other than private. In the example below, the access modifier declared on the set accessor has no meaning, since the set accessor is contained within a private class and thus cannot be seen by other classes outside of Class1. This effectively gives the set accessor the same access level as the get accessor.
public class Class1
{
private class Class2
{
public class Class3
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
internal set { this.name = value; }
}
}
}
}
4.Whenever the set accessor has protected or protected internal access, it should be referenced in the documentation. A protected or protected internal set accessor can always been seen by a class inheriting from the class containing the property.
internal class Class1
{
public class Class2
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
internal string Name
{
get { return this.name; }
protected set { this.name = value; }
}
}
private class Class3 : Class2
{
public Class3(string name) { this.Name = name; }
}
}
Install this: http://submain.com/products/ghostdoc.aspx
Right-click on the property, select 'Document This'.
Fill in the blanks.
According to MSDN, link, it appears there isn't an official tag for Class Properties. But, I would use something like this:
/// <summary>Here is an example of a propertylist:
/// <list type="Properties">
/// <item>
/// <description>Property 1.</description>
/// </item>
/// <item>
/// <description>Property 2.</description>
/// </item>
/// </list>
/// </summary>
I'd suggest to use StyleCop. It does not only enforce (a bit to strong for my taste) you to comment, but also gives you a hint how the comments should be startet.
According to C# documentation the keyword <value> is used to desrcibe properties.
Implementation would look like this:
/// <value>Description goes here.</value>
public string MyProperty { get; set; }
source: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments#d319-value
Using <summary> should work too and even though I'm still learning C# it seems the documentation is more of a recommendation than a PEP8-like guide. So I think as long as one's code is consistent it comes down to personal preference and/or company guidelines.

Create an Xml file from an object

I work as a web developer with a web designer and we usually do like this :
- I create the system , I generate some Xml files
- the designer display the xml files with xslt
Nothing new.
My problem is that I use Xml Serialization to create my xml files, but I never use Deserialization. So I'd like to know if there is a way to avoid fix like these :
empty setter for my property
empty parameter-less constructor
implement IXmlSerializable and throw "notimplementedexception" on deserialization
do a copy of the class with public fields
Ok mis-read your question first time around! Pretty sure there is no way to avoid this. There has to be a parameterless constructor and you can't serialize readonly properties. I think your only other option is DataContractSerializer.
http://blogs.mastronardi.be/Sandro/2007/08/22/CustomXMLSerializerBasedOnReflectionForSerializingPrivateVariables.aspx
This article describes creating a custom XML serialiser so you can serialise private fields - it may take a little bit of moulding to the form that you want, but it's easier than it looks (honest!) and it's a good start to writing your own serialiser / deserialiser that will serialise exactly what you want - and doesn't care about parameterless constructors or writeable properties.
The only other solution I can think of is to make a wrapper class for every serialisable class - but I don't know how good that would be in the long run. I just get the impression it's not good.
I know you don't want to add a parameterless constructor nor setters, but that's the only way to go with using the XmlSerializer. The good news is the parameterless constructor can be private and the setters can be empty and serialization will work. See thus:
namespace Aesop.Dto
{
using System;
using System.Xml.Serialization;
/// <summary>
/// Represents an Organization ID/Name combination.
/// </summary>
[Serializable]
public sealed class Org
{
/// <summary>
/// The organization's name.
/// </summary>
private readonly string name;
/// <summary>
/// The organization's ID.
/// </summary>
private readonly int id;
/// <summary>
/// Initializes a new instance of the <see cref="Org"/> class.
/// </summary>
/// <param name="name">The organization's name.</param>
/// <param name="id">The organization's ID.</param>
public Org(string name, int id)
{
this.name = name;
this.id = id;
}
/// <summary>
/// Prevents a default instance of the <see cref="Org"/> class from
/// being created.
/// </summary>
private Org()
{
}
/// <summary>
/// Gets or sets the organization's name.
/// </summary>
/// <value>The organization's name.</value>
[XmlAttribute]
public string Name
{
get
{
return this.name;
}
set
{
}
}
/// <summary>
/// Gets or sets the organization's ID.
/// </summary>
/// <value>The organization's ID.</value>
[XmlAttribute]
public int ID
{
get
{
return this.id;
}
set
{
}
}
}
}
Ok now i understand it. I don't think there can be any way to do it with XMLSerialization.
XMLSerialization need these information to re-populate the object. It does not know that some user never deserialize data. You might have to write some code to generate XML for your objects.
class Preferences
{
private const string filePreferences = "preferences.xml";
public Preferences() { }
public static Preferences Load()
{
Preferences pref = null;
if (File.Exists(Preferences.FileFullPath))
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Preferences));
using (var xmlReader = new System.Xml.XmlTextReader(Preferences.FileFullPath))
{
if (serializer.CanDeserialize(xmlReader))
{
pref = serializer.Deserialize(xmlReader) as Preferences;
}
}
}
return ((pref == null) ? new Preferences() : pref);
}
public void Save()
{
var preferencesFile = FileFullPath;
var preferencesFolder = Directory.GetParent(preferencesFile).FullName;
using (var fileStream = new FileStream(preferencesFile, FileMode.Create))
{
new System.Xml.Serialization.XmlSerializer(typeof(Preferences)).Serialize(fileStream, this);
}
}
}

Categories

Resources