I'm green on C# and .NET. I'm trying to tie other values to enumerations. I'm also trying to use a generic function to retrieve these values (from http://omegacoder.com/?p=28). The generic function does not work for me. It just returns the name of the Attribute type. I defined a custom Attribute, but I don't know how to get at the "Num" Property of it.
Basically, if I have a CRC polynomial enumeration value of Crc32ieeeNormal (1), I want to be able to retrieve the polynomial itself. These must be separate numbers, as the enumeration must be a single byte indicator.
I'm sure one could write separate structures, using the same numbers, etc., and that's what I'll do if I have to, but I'd like it all tied together.
using System;
using System.Linq;
namespace csAttributes
{
using System.Reflection; // to retrieve attribute values
class Program
{
static void Main(string[] args)
{
// I know this isn't right, but can't figure out how to get the attribute value....
CrcPolynomialTypesEnum crc = CrcPolynomialTypesEnum.Crc32ieeeNormal;
Console.WriteLine(Utility.ExtractAttribute<U64Attribute, CrcPolynomialTypesEnum>(crc));
Console.WriteLine(crc.ExtractAttribute<U64Attribute, CrcPolynomialTypesEnum>());
Console.ReadKey();
}
public enum CrcPolynomialTypesEnum : byte
{
// These must be explicitly defined as they are values used as indicators in a file.
[U64(0)]
Crc32InvalidOrNotSet = 0x00,
[U64(0x04C11DB7)]
Crc32ieeeNormal = 0x01,
[U64(0xEDB88320)]
Crc32ieeeReversed = 0x02,
[U64(0x1EDC6F41)]
Crc32CastagnoliNormal = 0x0A,
[U64(0x82F63B78)]
Crc32CastagnoliReversed = 0x0B
}
}
public static class Utility {
// from http://omegacoder.com/?p=28, 8/11/2017
/// <summary>
/// If an enum has a custom attrbute, this will returrn that attribute or null.
/// </summary>
/// <typeparam name="TCustomAttr">The type of the <code class="backtick">custom attribute</code> to extract from the enum.</typeparam>
/// <typeparam name="TEnumItance">The enum currently being viewed..</typeparam>
/// <param name="instance">The instance.</param>
/// <returns>The custom attribute (TCustomAttr) or null</returns>
public static TCustomAttr ExtractAttribute<TCustomAttr, TEnum>(this TEnum instance)
{
if (instance != null)
{
try
{
FieldInfo fieldInfo = instance.GetType()
.GetField(instance.ToString());
var attributes = fieldInfo.GetCustomAttributes(typeof(TCustomAttr), false)
.ToList();
if (attributes.Any())
return (TCustomAttr)attributes[0];
}
catch (Exception)
{
}
}
return default(TCustomAttr);
}
}
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
public class U64Attribute : Attribute
{
private UInt64 n;
public U64Attribute(UInt64 num)
{
n = num;
}
public UInt64 Num
{
get
{
return n;
}
}
}
}
Edit: Thank you, Kevin. Funny thing is, the "Num" property wasn't available earlier, but now it is. Must have been something I didn't have right earlier.
Edit 2: Using a generic here may be pointless, and a more specific function to retrieve the value seems to be better for this application.
Hopefully I read all the code right. What it looks like is that you are getting the class U64Attribute and trying to write it instead of the .Num...that should be an easy change, just use .Num appended to the ExtractAttribute call:
Console.WriteLine(Utility.ExtractAttribute<U64Attribute, CrcPolynomialTypesEnum>(crc).Num);
Related
My sample project is a MVC WebApi project.
The standard AuthorizeAttribute takes a Roles = "" or Users = "" parameter.
I didn't have a look at the implementation yet, but I don't want to either :)
Can you make this a little bit more dummy proof with expressions?
In a way that you get a compile time error when you decide to rename the attribute's "Roles" property to anything else?
public interface IControllerContext
{
int MagicNumber { get; }
int ScaryNumber { get; }
string Version { get; }
}
public class AppAuthorizationAttribute : FilterAttribute
{
public IControllerContext SomeControllerContext
{
get; // implementation omitted
}
// ...
}
// This is my sample class which needs to be validated using the attribute.
public class TestClass : BaseClass
{
[AppAuthorization((n) => n.MagicNumber == 13)] // or literally: which property and how to validate it"
protected void SomeMethodWhichRequiresPreProcessingValidation()
{
// do something.
// inside here I have access to an instance of ISomeContext
// and can do anything I want.
}
}
Optional bonus question: Would it somehow be possible to access a field of the current controller (not static) from within the attribute definition? It would be a cool thing to inject a controller field into the validation lambda expression. Something like that: AppAuthorization((controller) => controller.SomePropertyHere.MagicNumer == 13)
Attribute declaration can only use simple types and constants. The compiler will not allow you to use expressions so your planned approach will not work.
You also cannot reference fields or properties, just constants. The closest you could get is something along the lines of
public const MagicNumberPropertyName = "MagicNumber";
public enum Operator
{
Equals
}
[AppAuthorization(MagicNumberPropertyName, Operator.Equals, 13)]
protected void Method() {}
Here is the example , where you can use Enum for Authorise attribute , you can write your logic inside authorise
/// <summary>
/// Class RoleAuthorizeAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class RoleAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="RoleAuthorizeAttribute"/> class.
/// </summary>
/// <param name="roles">The roles.</param>
/// <exception cref="System.ArgumentException">The roles parameter may only contain enums;roles</exception>
public RoleAuthorizeAttribute(params object[] roles)
{
if (roles.Any(r => r.GetType().BaseType != typeof (Enum)))
{
throw new ArgumentException(
"The roles parameter may only contain enums",
"roles");
}
Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)).ToList());
}
}
Her is my situation where the enum values are stored as string in the Db. While retrieving I am getting an exception trying to convert string to enum using mytype that has the base class as EnumStringType.
Here is the error I am getting:
NHibernate.HibernateException : Can't Parse Enum4 as MyEnum
For example: The value coming from database is: "Enum4"
Valid enum values as per the code for MyEnum are:
Enum1
Enum2
Enum3
Somehow Enum4 got introduced in the Db before the code has accommodated the change. (I know crazy stuff happens)
The exception is normal because my Enum does not have this value coming from the database. But I don't want the user to get an exception. Instead default to the first value. (I agree this is not OK for some cases, but it is prevents exception which is more severe in my case)
If I am correct, GetInstance is the method that does this conversion from string to enum. Is there a TryGetXXXX of some sort to overcome this issue or how to solve it?
Thanks for your time!
Here is the Enum code I am playing with to address this issue:
public class EnumMappingBase : EnumStringType
{
public EnumMappingBase(Type type)
:base(type)
{
}
public override object GetInstance(object code)
{
return base.GetInstance(code); // Here is where I get the exception.
// I am thinking this is where capturing the exception and defaulting must happen.
// I wish I had a TryGetInstance() here or may be it is there and I am not aware.
}
public override object GetValue(object code)
{
return base.GetValue(code);
}
}
public enum MyEnum
{
Enum1,
Enum2,
Enum3
}
public class MyEnumType : EnumMappingBase
{
public MyEnumType()
: base(typeof(MyEnum))
{
}
}
Try to override GetInstance() in MyEnumType as follows:
public class MyEnumType : EnumMappingBase
{
public MyEnumType()
: base(typeof(MyEnum))
{}
public override object GetInstance(object code)
{
// Set the desired default value
MyEnum instanceValue = MyEnum.Enum1;
Enum.TryParse(code, true, out instanceValue);
return instanceValue;
}
}
I ran into the same problem today, and kay.herzams answer helped be out to create this class which can be used for any enumeration type.
It's a little bit more generic and flexible, so I thought I'd share it for anyone looking for a generic solution for this.
https://gist.github.com/flopes89/f6c4a079ee3b82c7a1df
using System;
using System.Reflection;
using log4net;
using NHibernate.Type;
/// <summary>
/// This class serves as a helper class to properly en- and decode enum values to/from strings
/// using hibernate. It is a generic class that can be used with any enumeration type and is designed
/// to replace the standard NHibernate.EnumStringType entirely.
///
/// This class should be used whenever an enumeration is consisted via NHibernate, because it has a failsafe
/// decoding of enumeration values from the database by defaulting them back to the given default value
/// when an unknown enumeration value is found in the database, which the default NHibernate.EnumStrinType does not
/// </summary>
/// <typeparam name="T">The enumeration type to use</typeparam>
public class EnumStringType<T> : EnumStringType where T : struct
{
private static ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private T _defaultValue;
/// <summary>
/// Create the type
/// </summary>
/// <param name="defaultValue_">Value to fallback to if an unknown enum value is found</param>
public EnumStringType(T defaultValue_)
: base(typeof(T))
{
_defaultValue = defaultValue_;
}
/// <summary>
/// Get the value of the given code
/// </summary>
/// <param name="code_">The code will be decoded using Enum.TryParse with the Type of this EnumStringType instance</param>
/// <returns>Either the defaultValue of this instance (logged with a warning) or the value of the code</returns>
public override object GetInstance(object code_)
{
T instanceValue = _defaultValue;
if (code_ != null && Enum.TryParse<T>(code_.ToString(), true, out instanceValue)) {
_logger.Debug("Decoded [" + typeof(T) + "] enum value [" + instanceValue + "]");
}
else {
_logger.Warn("Unknown [" + typeof(T) + "] enum value [" + code_ + "] found, defaulting to [" + instanceValue + "]");
}
return instanceValue;
}
}
Usage example:
public enum Permission
{
NULL,
EDIT
}
public class PermissionStringType : EnumStringType<Permission>
{
public PermissionStringType()
: base(Permission.NULL)
{
}
}
And the mapping can be done by:
<set name="Permissions" table="permissions" lazy="true">
<key column="role"/>
<element column="name" type="My.Namespace.PermissionEnumStringType,MyAssemblyName"/>
</set>
I've got a WCF DataContract that looks like the following:
namespace MyCompanyName.Services.Wcf
{
[DataContract(Namespace = "http://mycompanyname/services/wcf")]
[Serializable]
public class DataContractBase
{
[DataMember]
public DateTime EditDate { get; set; }
// code omitted for brevity...
}
}
When I add a reference to this service in Visual Studio, this proxy code is generated:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://mycompanyname/services/wcf")]
public partial class DataContractBase : object, System.ComponentModel.INotifyPropertyChanged {
private System.DateTime editDateField;
private bool editDateFieldSpecified;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public System.DateTime EditDate {
get {
return this.editDateField;
}
set {
this.editDateField = value;
this.RaisePropertyChanged("EditDate");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool EditDateSpecified {
get {
return this.editDateFieldSpecified;
}
set {
this.editDateFieldSpecified = value;
this.RaisePropertyChanged("EditDateSpecified");
}
}
// code omitted for brevity...
}
As you can see, besides generating a backing property for EditDate, an additional <propertyname>Specified property is generated. All good, except that when I do the following:
DataContractBase myDataContract = new DataContractBase();
myDataContract.EditDate = DateTime.Now;
new MyServiceClient.Update(new UpdateRequest(myDataContract));
the EditDate was not getting picked up by the endpoint of the service (does not appear in the transmitted XML).
I debugged the code and found that, although I was setting EditDate, the EditDateSpecified property wasn't being set to true as I would expect; hence, the XML serializer was ignoring the value of EditDate, even though it's set to a valid value.
As a quick hack I modified the EditDate property to look like the following:
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public System.DateTime EditDate {
get {
return this.editDateField;
}
set {
this.editDateField = value;
// hackhackhack
if (value != default(System.DateTime))
{
this.EditDateSpecified = true;
}
// end hackhackhack
this.RaisePropertyChanged("EditDate");
}
}
Now the code works as expected, but of course every time I re-generate the proxy, my modifications are lost. I could change the calling code to the following:
DataContractBase myDataContract = new DataContractBase();
myDataContract.EditDate = DateTime.Now;
myDataContract.EditDateSpecified = true;
new MyServiceClient.Update(new UpdateRequest(myDataContract));
but that also seems like a hack-ish waste of time.
So finally, my question: does anyone have a suggestion on how to get past this unintuitive (and IMO broken) behavior of the Visual Studio service proxy generator, or am I simply missing something?
It might be a bit unintuitive (and caught me off guard and reeling, too!) - but it's the only proper way to handle elements that might or might not be specified in your XML schema.
And it also might seem counter-intuitive that you have to set the xyzSpecified flag yourself - but ultimately, this gives you more control, and WCF is all about the Four Tenets of SOA of being very explicit and clear about your intentions.
So basically - that's the way it is, get used to it :-) There's no way "past" this behavior - it's the way the WCF system was designed, and for good reason, too.
What you always can do is catch and handle the this.RaisePropertyChanged("EditDate"); event and set the EditDateSpecified flag in an event handler for that event.
try this
[DataMember(IsRequired=true)]
public DateTime EditDate { get; set; }
This should omit the EditDateSpecified property since the field is specified as required
Rather than change the setters of the autogenerated code, you can use an extension class to 'autospecify' (bind the change handler event). This could have two implementations -- a "lazy" one (Autospecify) using reflection to look for fieldSpecified based on the property name, rather than listing them all out for each class in some sort of switch statement like Autonotify:
Lazy
public static class PropertySpecifiedExtensions
{
private const string SPECIFIED_SUFFIX = "Specified";
/// <summary>
/// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically set any xxxSpecified fields when a property is changed. "Lazy" via reflection.
/// </summary>
/// <param name="entity">the entity to bind the autospecify event to</param>
/// <param name="specifiedSuffix">optionally specify a suffix for the Specified property to set as true on changes</param>
/// <param name="specifiedPrefix">optionally specify a prefix for the Specified property to set as true on changes</param>
public static void Autospecify(this INotifyPropertyChanged entity, string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null)
{
entity.PropertyChanged += (me, e) =>
{
foreach (var pi in me.GetType().GetProperties().Where(o => o.Name == specifiedPrefix + e.PropertyName + specifiedSuffix))
{
pi.SetValue(me, true, BindingFlags.SetField | BindingFlags.SetProperty, null, null, null);
}
};
}
/// <summary>
/// Create a new entity and <see cref="Autospecify"/> its properties when changed
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="specifiedSuffix"></param>
/// <param name="specifiedPrefix"></param>
/// <returns></returns>
public static T Create<T>(string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null) where T : INotifyPropertyChanged, new()
{
var ret = new T();
ret.Autospecify(specifiedSuffix, specifiedPrefix);
return ret;
}
}
This simplifies writing convenience factory methods like:
public partial class MyRandomClass
{
/// <summary>
/// Create a new empty instance and <see cref="PropertySpecifiedExtensions.Autospecify"/> its properties when changed
/// </summary>
/// <returns></returns>
public static MyRandomClass Create()
{
return PropertySpecifiedExtensions.Create<MyRandomClass>();
}
}
A downside (other than reflection, meh) is that you have to use the factory method to instantiate your classes or use .Autospecify before (?) you make any changes to properties with specifiers.
No Reflection
If you don't like reflection, you could define another extension class + interface:
public static class PropertySpecifiedExtensions2
{
/// <summary>
/// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically call each class's <see cref="IAutoNotifyPropertyChanged.Autonotify"/> method on the property name.
/// </summary>
/// <param name="entity">the entity to bind the autospecify event to</param>
public static void Autonotify(this IAutoNotifyPropertyChanged entity)
{
entity.PropertyChanged += (me, e) => ((IAutoNotifyPropertyChanged)me).WhenPropertyChanges(e.PropertyName);
}
/// <summary>
/// Create a new entity and <see cref="Autonotify"/> it's properties when changed
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T Create<T>() where T : IAutoNotifyPropertyChanged, new()
{
var ret = new T();
ret.Autonotify();
return ret;
}
}
/// <summary>
/// Used by <see cref="PropertySpecifiedExtensions.Autonotify"/> to standardize implementation behavior
/// </summary>
public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged
{
void WhenPropertyChanges(string propertyName);
}
And then each class themselves defines the behavior:
public partial class MyRandomClass: IAutoNotifyPropertyChanged
{
public void WhenPropertyChanges(string propertyName)
{
switch (propertyName)
{
case "field1": this.field1Specified = true; return;
// etc
}
}
}
The downside to this is, of course, magic strings for property names making refactoring difficult, which you could get around with Expression parsing?
Further information
On the MSDN here
In her answer, Shreesha explains that:
"Specified" fields are only generated on optional parameters that are structs. (int, datetime, decimal etc). All such variables will have additional variable generated with the name Specified.
This is a way of knowing if a parameter is really passed between the client and the server.
To elaborate, an optional integer, if not passed, would still have the dafault value of 0. How do you differentiate between this and the one that was actually passed with a value 0 ? The "specified" field lets you know if the optional integer is passed or not. If the "specified" field is false, the value is not passed across. If it true, the integer is passed.
so essentially, the only way to have these fields set is to set them manually, and if they aren't set to true for a field that has been set, then that field will be missed out in the SOAP message of the web-service call.
What I did in the end was build a method to loop through all the members of the object, and if the property has been set, and if there is a property called name _Specified then set that to true.
Ian,
Please ignore my previous answers, was explaining how to suck eggs. I've voted to delete them.
Could you tell me which version of Visual Studio you're using, please?
In VS2005 client - in the generated code, I get the <property>Specified flags, but no event raised on change of values. To pass data I have to set the <property>Specified flag.
In Visual Web Developer 2008 Express client - in the generated code, I get no <property>Specified flags, but I do get the event on change of value.
Seems to me that this functionality has evolved and the Web Dev 2008 is closer to what you're after and is more intuitive, in that you don't need to set flags once you've set a value.
Bowthy
Here's a simple project that can modify the setters in generated WCF code for optional properties to automatically set the *Specified flags to true when setting the related value.
https://github.com/b9chris/WcfClean
Obviously there are situations where you want manual control over the *Specified flag so I'm not recommending it to everyone, but in most simple use cases the *Specified flags are just an extra nuisance and automatically setting them saves time, and is often more intuitive.
Note that Mustafa Magdy's comment on another answer here will solve this for you IF you control the Web Service publication point. However, I usually don't control the Web Service publication and am just consuming one, and have to cope with the *Specified flags in some simple software where I'd like this automated. Thus this tool.
Change proxy class properties to nullable type
ex :
bool? confirmed
DateTime? checkDate
I have a dictionary of strings that i want the user to be able to add/remove info from then store it for them so it they can access it the next time the program restarts
I am unclear on how i can store a dictionary as a setting. I see that under system.collections.special there is a thing called a stringdictionary but ive read that SD are outdated and shouldn't be used.
also in the future i may have need to store a dictionary that is not strings only (int string)
how would you store a dictionary in the settings file for a .net application?
You can use this class derived from StringDictionary. To be useful for application settings it implements IXmlSerializable.
Or you can use similar approach to implement your own XmlSerializable class.
public class SerializableStringDictionary : System.Collections.Specialized.StringDictionary, System.Xml.Serialization.IXmlSerializable
{
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
while (reader.Read() &&
!(reader.NodeType == System.Xml.XmlNodeType.EndElement && reader.LocalName == this.GetType().Name))
{
var name = reader["Name"];
if (name == null)
throw new FormatException();
var value = reader["Value"];
this[name] = value;
}
}
public void WriteXml(System.Xml.XmlWriter writer)
{
foreach (System.Collections.DictionaryEntry entry in this)
{
writer.WriteStartElement("Pair");
writer.WriteAttributeString("Name", (string)entry.Key);
writer.WriteAttributeString("Value", (string)entry.Value);
writer.WriteEndElement();
}
}
}
Resulting XML fragment will look similar to:
...
<setting name="PluginSettings" serializeAs="Xml">
<value>
<SerializableStringDictionary>
<Pair Name="property1" Value="True" />
<Pair Name="property2" Value="05/01/2011 0:00:00" />
</SerializableStringDictionary>
</value>
</setting>
...
The simplest answer would be to use a row & column delimiter to convert your dictionary to a single string. Then you just need to store 1 string in the settings file.
If you don't need to use the settings designer or edit your settings with a text editor, you can create a simple class that derives from ApplicationSettingsBase:
namespace MyNamespace
{
using System.Collections.Generic;
using System.Configuration;
/// <summary>
/// Persistent store for my parameters.
/// </summary>
public class MySettings : ApplicationSettingsBase
{
/// <summary>
/// The instance lock.
/// </summary>
private static readonly object InstanceLock = new object();
/// <summary>
/// The instance.
/// </summary>
private static MySettings instance;
/// <summary>
/// Prevents a default instance of the <see cref="MySettings"/> class
/// from being created.
/// </summary>
private MySettings()
{
// don't need to do anything
}
/// <summary>
/// Gets the singleton.
/// </summary>
public static MySettings Instance
{
get
{
lock (InstanceLock)
{
if (instance == null)
{
instance = new MySettings();
}
}
return instance;
}
}
/// <summary>
/// Gets or sets the parameters.
/// </summary>
[UserScopedSetting]
[SettingsSerializeAs(SettingsSerializeAs.Binary)]
public Dictionary<string, string> Parameters
{
get
{
return (Dictionary<string, string>)this["Parameters"];
}
set
{
this["Parameters"] = value;
}
}
}
}
The real trick is the [SettingsSerializeAs(SettingsSerializeAs.Binary)] attribute. Most (all?) classes can get serialized this way where SettingsSerializeAs.String or SettingsSerializeAs.Xml wont work for a Dictionary.
Use this in your code as you would normal settings:
// this code untested...
MySettings.Instance.Parameters["foo"] = "bar";
MySettings.Instance.Parameters.Save();
MySettings.Instance.Parameters.Reload();
string bar;
if (!MySettings.Instance.Parameters.TryGetValue("foo", out bar))
{
throw new Exception("Foobar");
}
If you want the Dictionary to serialize into something user editable, you must derive from Dictionary and play with TypeConverter (see Using Custom Classes with Application Settings).
Other than doing something like David's suggests, I would look into alternate storage for the Dictionary. Ultimately the Settings object serializes to disk.
Have you considered using XML to store your dictionary? That would provide a certain amount of extensibility if in the future you decide you want to be able to store other types of dictionaries. You might do something like:
<dictionary>
<entry key="myKey">
[whatever data you like]
</entry>
</dictionary>
Might be overkill, but you'd also be prepared in the case that you wanted to store more complex data, like custom objects.
You can also use a System.Collections.Specialized.StringCollection by putting key on even index and values on odd index.
/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>
private static string getValue(System.Collections.Specialized.StringCollection list, string key)
{
for (int i = 0; i * 2 < list.Count; i++)
{
if (list[i] == key)
{
return list[i + 1];
}
}
return null;
}
/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>
private static void setValue(System.Collections.Specialized.StringCollection list, string key, string value)
{
for (int i = 0; i * 2 < list.Count; i++)
{
if (list[i] == key)
{
list[i + 1] = value;
return;
}
}
list.Add(key);
list.Add(value);
}
You could create a custom class that exposes a Dictionary as a public property. Then you can specify this custom type as the type for your setting.
Edit:
I have just read that, for some reason, a generic dictionary cannot be XML-serialized, so my solution will probably not work (I haven't tested it though...). That's strange, because a generic list can be serialized without any problem.
You could still create a custom class that can be set as a user setting, but you will need to have a list exposed as a property instead of a dictionary.
Edit: This will return a Hashtable (for whatever reason, despite being a 'DictionarySectionHandler'). However, being that Hashtables and Dictionaries are so similar, it shouldn't be a large issue (though I realize Dictionaries are newer, parameterized, etc; I would have preferred dicitonaries myself, but this is what .NET gives us).
The best answer I just found for this is here. It returns a typesafe collection witout any muddling in code to transform it, and you create an obvious (and simple) collection in your .config file. I'm using this and it's quite straight forward for any future programmer (including yourself). It allows for stronger typing and more flexibility, without any overly-complicated and unnecessary parsing.
You can store a StringCollection. It is similar to this solution.
I made 2 extension methods to convert between StringCollection and a Dictionary. This is the easiest way I could think of.
public static class Extender
{
public static Dictionary<string, string> ToDictionary(this StringCollection sc)
{
if (sc.Count % 2 != 0) throw new InvalidDataException("Broken dictionary");
var dic = new Dictionary<string, string>();
for (var i = 0; i < sc.Count; i += 2)
{
dic.Add(sc[i], sc[i + 1]);
}
return dic;
}
public static StringCollection ToStringCollection(this Dictionary<string, string> dic)
{
var sc = new StringCollection();
foreach (var d in dic)
{
sc.Add(d.Key);
sc.Add(d.Value);
}
return sc;
}
}
class Program
{
static void Main(string[] args)
{
//var sc = new StringCollection();
//sc.Add("Key01");
//sc.Add("Val01");
//sc.Add("Key02");
//sc.Add("Val02");
var sc = Settings.Default.SC;
var dic = sc.ToDictionary();
var sc2 = dic.ToStringCollection();
Settings.Default.SC = sc2;
Settings.Default.Save();
}
}
I know I could have an attribute but that's more work than I want to go to... and not general enough.
I want to do something like
class Whotsit
{
private string testProp = "thingy";
public string TestProp
{
get { return testProp; }
set { testProp = value; }
}
}
...
Whotsit whotsit = new Whotsit();
string value = GetName(whotsit.TestProp); //precise syntax up for grabs..
where I'd expect value to equal "TestProp"
but I can't for the life of me find the right reflection methods to write the GetName method...
EDIT: Why do I want to do this? I have a class to store settings read from a 'name', 'value' table. This is populated by a generalised method based upon reflection. I'd quite like to write the reverse...
/// <summary>
/// Populates an object from a datatable where the rows have columns called NameField and ValueField.
/// If the property with the 'name' exists, and is not read-only, it is populated from the
/// valueField. Any other columns in the dataTable are ignored. If there is no property called
/// nameField it is ignored. Any properties of the object not found in the data table retain their
/// original values.
/// </summary>
/// <typeparam name="T">Type of the object to be populated.</typeparam>
/// <param name="toBePopulated">The object to be populated</param>
/// <param name="dataTable">'name, 'value' Data table to populate the object from.</param>
/// <param name="nameField">Field name of the 'name' field'.</param>
/// <param name="valueField">Field name of the 'value' field.</param>
/// <param name="options">Setting to control conversions - e.g. nulls as empty strings.</param>
public static void PopulateFromNameValueDataTable<T>
(T toBePopulated, System.Data.DataTable dataTable, string nameField, string valueField, PopulateOptions options)
{
Type type = typeof(T);
bool nullStringsAsEmptyString = options == PopulateOptions.NullStringsAsEmptyString;
foreach (DataRow dataRow in dataTable.Rows)
{
string name = dataRow[nameField].ToString();
System.Reflection.PropertyInfo property = type.GetProperty(name);
object value = dataRow[valueField];
if (property != null)
{
Type propertyType = property.PropertyType;
if (nullStringsAsEmptyString && (propertyType == typeof(String)))
{
value = TypeHelper.EmptyStringIfNull(value);
}
else
{
value = TypeHelper.DefaultIfNull(value, propertyType);
}
property.SetValue(toBePopulated, System.Convert.ChangeType(value, propertyType), null);
}
}
}
FURTHER EDIT: I am just in code, have an instance of Whotsit and I want to get the text string of the 'TestProp' property. It seems kind of weird I know, I can just use the literal "TestProp" - or in the case of my class to datatable function I'd be in a foreach loop of PropertyInfos. I was just curious...
The original code had string constants, which I found clumsy.
No, there's nothing to do this. The expression whotsit.TestProp will evaluate the property. What you want is the mythical "infoof" operator:
// I wish...
MemberInfo member = infoof(whotsit.TestProp);
As it is, you can only use reflection to get the property by name - not from code. (Or get all the properties, of course. It still doesn't help you with your sample though.)
One alternative is to use an expression tree:
Expression<Func<string>> = () => whotsit.TestProp;
then examine the expression tree to get the property.
If none of this helps, perhaps you could tell us more about why you want this functionality?
It is possible (without reflection) but only with latest C# 3.0
quick & very very dirty
class Program
{
static void Main()
{
string propertyName = GetName(() => AppDomain.CurrentDomain);
Console.WriteLine(propertyName); // prints "CurrentDomain"
Console.ReadLine();
}
public static string GetName(Expression<Func<object>> property)
{
return property.Body.ToString().Split('.').Last();
}
}
Update: I've just realized that Jon Skeet (anyone surprised? :) has covered this possibility already but I'll keep my answer here just in case someone is interested in some example to start with.
I don't think it's possible, the only way to do this is to iterate over properties:
class TestClass
{
private string _field;
public string MyProperty
{
get { return _field; }
}
}
class Program
{
static void Main(string[] args)
{
TestClass test = new TestClass();
PropertyInfo[] info = test.GetType().GetProperties();
foreach(PropertyInfo i in info)
Console.WriteLine(i.Name);
Console.Read();
}
}
Kpollack, you said in an earlier comment:
which still won't give me the ability to get the name of a property from an instance of it.
This leads me to believe that you somehow have a reference to a property. How did you get this reference? What is its type? Could you provide a code sample? If it's a PropertyInfo object, you already have what you need; since this doesn't appear to be the case, we're dealing with something else, and I'd be very interested to see what it is that you do have to work with.
P.S. Forgive me for seeming obtuse: it's early, I haven't had enough coffee, and I don't have my IDE in front of me. :-/
What you are trying to do is not possible. Using reflection you can:
Get a property, field or method's details from it's string name.
Get a list of all properties, fields or methods for a given type.
Your GetName method, when called, is passed a string. The GetMethod will know about the string but retains no source property meta data.
Out of interest, why are you trying to do this?
FYI, I tried to serialize it to see if, by chance, that contains the property name, but no luck.
Non-working code below:
Whotsit w = new Whotsit();
XmlSerializer xs = new XmlSerializer(w.TestProp.GetType());
TextWriter sw = new StreamWriter(#"c:\TestProp.xml");
xs.Serialize(sw, w.TestProp);
sw.Close();
The GetProperties on the Type class will give you the list of properties on that type.
Type t = whotsit.GetType();
PropertyInfo[] pis = t.GetProperties();