I have a class that includes several hundred Properties. Each of the properties was declared with a [CategoryAttribute("My Category Name")] attribute clause so that it will display nicely in a PropertyGrid. I would like to make use of this same CategoryAttribute attribute to set the values of all the properties in the class that were labeled with specific categoryAttribute catagories. The code below compiles and runs but it doesn't accomplish the task because att_coll doesn't contain the CategoryAttribute attribute that I expected that it would. Does anyone know how to do this? Thanks so much.
class my_class
{
[CategoryAttribute("Category One")]
public int property_1
{
get { return _property_1; }
set { _property_1 = value; }
}
[CategoryAttribute("Category One")]
public int property_2
{
get { return _property_2; }
set { _property_2 = value; }
}
}
void ClearCatagory(string category_name)
{
CategoryAttribute target_attribute = new CategoryAttribute(category_name);
Type my_class_type = my_class.GetType();
PropertyInfo[] prop_info_array = my_class_type.GetProperties();
foreach (PropertyInfo prop_info in prop_info_array)
{
AttributeCollection att_coll = TypeDescriptor.GetAttributes(prop_info);
CategoryAttribute ca = (CategoryAttribute) att_col[typeof(CategoryAttribute)];
if (ca.Equals(target_attribute))
{
prop_info.SetValue(my_class, 0, null);
}
}
}
Use the MemberInfo.GetCustomAttributes instance method instead of TypeDescriptor.GetAttriburtes.
The call would be object[] attributes = prop_info.GetCustomAttributes(typeof(CategoryAttriute), false).
Or you could use TypeDescriptor.GetProperties instead of Type.GetProperties You shouldn't switch between using reflection and TypeDescriptor.
Also, the documentation for Category.Equals isn't exactly clear, but likely it implements reference equality (which is the default for C# unless a class specifically overrides it). Which means that Equals will only return true if the instances being compared are exactly the same regardless of the value of Category. If that is the case, then ca.Equals(target_attribute) will always be false because the references are different objects.
Try instead comparing the string value stored in the Category value. Strings implement value equality so that String.Equals compares the values stored in the stings.
So replace
if (ca.Equals(target_attribute))
with
if (ca.Cateogry.Equals(category_name))
Thanks so much to shf301 for solving this. Here is a working version of the code:
class my_class
{
[CategoryAttribute("Category One")]
public int property_1
{
get { return _property_1; }
set { _property_1 = value; }
}
}
void ClearCatagory(string category_name)
{
Type my_class_type = my_class.GetType();
PropertyInfo[] prop_info_array = my_class_type.GetProperties();
foreach (PropertyInfo prop_info in prop_info_array)
{
CategoryAttribute[] attributes = (CategoryAttribute[]) prop_info.GetCustomAttributes(typeof(CategoryAttribute), false);
foreach(CategoryAttribute ca in attributes)
{
if (ca.Category == category_name)
{
prop_info.SetValue(my_class, 0, null);
}
}
}
}
public class cls
{
[Category("Default")]
[DisplayName("Street")]
public string Street { get; set; }
}
foreach (PropertyInfo propinf in cls.GetProperties())
{
var category = prop.CustomAttributes.Where(x => x.AttributeType ==typeof(CategoryAttribute)).First();
sCategory = category.ConstructorArguments[0].Value.ToString();
}
This is how we could get value of CustomAttribute
Related
I have a MerchantWSBO and MerchantWSVO classes.
MerchantWSBO has a property of a type of MerchantWSVO.
I need to get a value of the property of a MerchantWSVO.
So, I have a code defining both classes(classes are coming through a WebReference from a 3rd party)
public MerchantWSBO {
private MerchantWSVO overviewField;
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public MerchantWSVO overview {
get {
return this.overviewField;
}
set {
this.overviewField = value;
}
}
}
public MerchantWSVO{
private System.Nullable<bool> discoverRetainedField;
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool discoverRetainedSpecified {
get {
return this.discoverRetainedFieldSpecified;
}
set {
this.discoverRetainedFieldSpecified = value;
}
}
}
I have the following method where I need to get the property value of dicoverRetained using reflection:
private string ClassToXML(Object classObject)
{
MerchantTest mt = new MerchantTest();
if(classObject is MerchantWSBO)
{
classObject.GetType().GetProperty("overviewField").GetValue(new MerchantWSVO, null);
mt.overview.discoverRetained = //need to get the value
}
var myString = new System.IO.StringWriter();
var serializer = new XmlSerializer(classObject.GetType());
serializer.Serialize(myString, classObject);
return myString.ToString();
}
Based on a parameter classObject which in this case can be of two types, I need to get a value from a property.
How can I do that?
You don't need reflection at all, simply cast the object to the correct type. Pattern matching helps here.
if(classObject is MerchantWSBO wsbo)
{
Console.WriteLine(wsbo.overview.discoverRetained);
}
or on older C# versions:
var wsbo = classObject as MerchantWSBO;
if(wsbo != null)
{
Console.WriteLine(wsbo.overview.discoverRetained);
}
I have a class with 10 properties and I am using reflection to assign values to these properties. My Property names are like,
private double? _col1;
public double? Col1
{
get
{
return _col1;
}
set
{
_col1 = value;
OnPropertyChanged("Col1");
}
}
private double? _col2;
public double? Col2
{
get
{
return _col2;
}
set
{
_col2 = value;
OnPropertyChanged("Col2");
}
}
And I am using them like,
MyClass md = new MyClass();
PropertyInfo[] properties = typeof(MyClass).GetProperties();
{
foreach (PropertyInfo property in properties)
{
double? d1 = from some method();
if (property.PropertyType == typeof(Double?))
{
if (property.GetValue(md) == null)
{
property.SetValue(md, d1);
}
}
}
}
Here I want to use an orderby property name of the class. How ??
PropertyInfo[] properties = typeof(MyClass).GetProperties();
You need to sort properties alphabetically based on their name.
Add the following line before the foreach() loop
Array.Sort(properties, (x,y)=> x.Name.CompareTo(y.Name)
I think this is an XY problem.
I think what you actually need could be something like this:
public class MyClass
{
public ObservableCollection<double?> Cols { get; }
public MyClass(int initialSize)
{
if(initialSize < 0)
{
throw new ArgumentOutOfRangeException(nameof(initialSize));
}
Cols = new(Enumerable.Repeat((double?)null, initialSize));
}
}
Then, ditching the reflection, you can just use a for loop on the observable collection. The observable collection is great cause it already raises events informing about changes.
MyClass md = new MyClass(2);
for (int i = 0; i < md.Cols.Count; i++)
{
double? d1 = from some method();
if (md.Cols[i] is null)
{
md.Cols[i] = d1;
}
}
Then, if someone needs to listen to changes made to Cols, they subscribe to the Cols.CollectionChanged event.
Order of properties in reflection is the same as in their declaration in program code. Property declared first is first. Property declared second is second. I have never tested myself, but in case of partial classes, when a class is split into several files, the situation might be tricky.
how we can ensure that it takes first Col1, then secondly Col2, etc ??
To ensure, You should use names of the properties. Example
if (property.Name == nameof(MyClass.Col1))
{
property.SetValue(instanceOfClassName, value);
}
Alternative for if is switch statement
switch (property.Name)
{
case nameof(MyClass.Col1):
break;
case nameof(MyClass.Col2):
break;
}
If you want to order the properties by name then simply use OrderBy LINQ query
var orderedByNameProperties = properties.OrderBy(property => property.Name);
I would like to get property name when I'm in it via reflection. Is it possible?
I have code like this:
public CarType Car
{
get { return (Wheel) this["Wheel"];}
set { this["Wheel"] = value; }
}
And because I need more properties like this I would like to do something like this:
public CarType Car
{
get { return (Wheel) this[GetThisPropertyName()];}
set { this[GetThisPropertyName()] = value; }
}
Since properties are really just methods you can do this and clean up the get_ returned:
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var x = p.Something;
Console.ReadLine();
}
public string Something
{
get
{
return MethodBase.GetCurrentMethod().Name;
}
}
}
If you profile the performance you should find MethodBase.GetCurrentMethod() is miles faster than StackFrame. In .NET 1.1 you will also have issues with StackFrame in release mode (from memory I think I found it was 3x faster).
That said I'm sure the performance issue won't cause too much of a problem- though an interesting discussion on StackFrame slowness can be found here.
I guess another option if you were concerned about performance would be to create a Visual Studio Intellisense Code Snippet that creates the property for you and also creates a string that corresponds to the property name.
Slightly confusing example you presented, unless I just don't get it.
From C# 6.0 you can use the nameof operator.
public CarType MyProperty
{
get { return (CarType)this[nameof(MyProperty)]};
set { this[nameof(MyProperty)] = value]};
}
If you have a method that handles your getter/setter anyway, you can use the C# 4.5 CallerMemberName attribute, in this case you don't even need to repeat the name.
public CarType MyProperty
{
get { return Get<CarType>(); }
set { Set(value); }
}
public T Get<T>([CallerMemberName]string name = null)
{
return (T)this[name];
}
public void Set<T>(T value, [CallerMemberName]string name = null)
{
this[name] = value;
}
I'd like to know more about the context in which you need it since it seems to me that you should already know what property you are working with in the property accessor. If you must, though, you could probably use MethodBase.GetCurrentMethod().Name and remove anything after get_/set_.
Update:
Based on your changes, I would say that you should use inheritance rather than reflection. I don't know what data is in your dictionary, but it seems to me that you really want to have different Car classes, say Sedan, Roadster, Buggy, StationWagon, not keep the type in a local variable. Then you would have implementations of methods that do the proper thing for that type of Car. Instead of finding out what kind of car you have, then doing something, you then simply call the appropriate method and the Car object does the right thing based on what type it is.
public interface ICar
{
void Drive( decimal velocity, Orientation orientation );
void Shift( int gear );
...
}
public abstract class Car : ICar
{
public virtual void Drive( decimal velocity, Orientation orientation )
{
...some default implementation...
}
public abstract void Shift( int gear );
...
}
public class AutomaticTransmission : Car
{
public override void Shift( int gear )
{
...some specific implementation...
}
}
public class ManualTransmission : Car
{
public override void Shift( int gear )
{
...some specific implementation...
}
}
Use MethodBase.GetCurrentMethod() instead!
Reflection is used to do work with types that can't be done at compile time. Getting the name of the property accessor you're in can be decided at compile time so you probably shouldn't use reflection for it.
You get use the accessor method's name from the call stack using System.Diagnostics.StackTrace though.
string GetPropertyName()
{
StackTrace callStackTrace = new StackTrace();
StackFrame propertyFrame = callStackTrace.GetFrame(1); // 1: below GetPropertyName frame
string properyAccessorName = propertyFrame.GetMethod().Name;
return properyAccessorName.Replace("get_","").Replace("set_","");
}
FWIW I implemented a system like this:
[CrmAttribute("firstname")]
public string FirstName
{
get { return GetPropValue<string>(MethodBase.GetCurrentMethod().Name); }
set { SetPropValue(MethodBase.GetCurrentMethod().Name, value); }
}
// this is in a base class, skipped that bit for clairty
public T GetPropValue<T>(string propName)
{
propName = propName.Replace("get_", "").Replace("set_", "");
string attributeName = GetCrmAttributeName(propName);
return GetAttributeValue<T>(attributeName);
}
public void SetPropValue(string propName, object value)
{
propName = propName.Replace("get_", "").Replace("set_", "");
string attributeName = GetCrmAttributeName(propName);
SetAttributeValue(attributeName, value);
}
private static Dictionary<string, string> PropToAttributeMap = new Dictionary<string, string>();
private string GetCrmAttributeName(string propertyName)
{
// keyName for our propertyName to (static) CrmAttributeName cache
string keyName = this.GetType().Name + propertyName;
// have we already done this mapping?
if (!PropToAttributeMap.ContainsKey(keyName))
{
Type t = this.GetType();
PropertyInfo info = t.GetProperty(propertyName);
if (info == null)
{
throw new Exception("Cannot find a propety called " + propertyName);
}
object[] attrs = info.GetCustomAttributes(false);
foreach (object o in attrs)
{
CrmAttributeAttribute attr = o as CrmAttributeAttribute ;
if (attr != null)
{
// found it. Save the mapping for next time.
PropToAttributeMap[keyName] = attr.AttributeName;
return attr.AttributeName;
}
}
throw new Exception("Missing MemberOf attribute for " + info.Name + "." + propertyName + ". Could not auto-access value");
}
// return the existing mapping
string result = PropToAttributeMap[keyName];
return result;
}
There's also a custom attribute class called CrmAttributeAttribute.
I'd strongly recommend against using GetStackFrame() as part of your solution, my original version of the solution was originally the much neater:
return GetPropValue<string>();
But it was 600x slower than the version above.
Solution # 1
var a = nameof(SampleMethod); //a == SampleMethod
var b = nameof(SampleVariable); //b == SampleVariable
var c = nameof(SampleProperty); //c == SampleProperty
Solution # 2
MethodBase.GetCurrentMethod().Name; // Name of method in which you call the code
MethodBase.GetCurrentMethod().Name.Replace("set_", "").Replace("get_", ""); // current Property
Solution # 3
from StackTrace:
public static class Props
{
public static string CurrPropName =>
(new StackTrace()).GetFrame(1).GetMethod().Name.Replace("set_", "").Replace("get_", "");
public static string CurrMethodName =>
(new StackTrace()).GetFrame(1).GetMethod().Name;
}
you just need to call Props.CurrPropName or Props.CurrMethodName
Solution # 4
Solution for .NET 4.5+:
public static class Props
{
public static string GetCallerName([System.Runtime.CompilerServices.CallerMemberName] String propertyName = "")
{
return propertyName;
}
}
usage: Props.GetCallerName();
Yes, it is!
string test = "test string";
Type type = test.GetType();
PropertyInfo[] propInfos = type.GetProperties();
for (int i = 0; i < propInfos.Length; i++)
{
PropertyInfo pi = (PropertyInfo)propInfos.GetValue(i);
string propName = pi.Name;
}
Try using System.Diagnostics.StackTrace to reflect on the call stack. The property should be somewhere in the call stack (probably at the top if you're calling it directly from the property's code).
What is the best way to localize enumeration descriptions in .net?
(See Adding descriptions to enumeration constants for enum description example)
Ideally I would like something that uses ResourceManager and resource files so it fits in with how other areas of the app are localized.
This is what I ended up going with, I didn't see the value in adding a custom attribute class to hold a resource key and then looking up into the resource files - why not just use the enums typename + value as a resource key?
using System;
using System.Resources;
using System.Reflection;
public class MyClass
{
enum SomeEnum {Small,Large};
private ResourceManager _resources = new ResourceManager("MyClass.myResources",
System.Reflection.Assembly.GetExecutingAssembly());
public string EnumDescription(Enum enumerator)
{
string rk = String.Format("{0}.{1}",enumerator.GetType(),enumerator);
string localizedDescription = _resources.GetString(rk);
if (localizedDescription == null)
{
// A localized string was not found so you can either just return
// the enums value - most likely readable and a good fallback.
return enumerator.ToString();
// Or you can return the full resourceKey which will be helpful when
// editing the resource files(e.g. MyClass+SomeEnum.Small)
// return resourceKey;
}
else
return localizedDescription;
}
void SomeRoutine()
{
// Looks in resource file for a string matching the key
// "MyClass+SomeEnum.Large"
string s1 = EnumDescription(SomeEnum.Large);
}
}
My solution, using native decription attribute:
public class LocalizedEnumAttribute : DescriptionAttribute
{
private PropertyInfo _nameProperty;
private Type _resourceType;
public LocalizedEnumAttribute(string displayNameKey)
: base(displayNameKey)
{
}
public Type NameResourceType
{
get
{
return _resourceType;
}
set
{
_resourceType = value;
_nameProperty = _resourceType.GetProperty(this.Description, BindingFlags.Static | BindingFlags.Public);
}
}
public override string Description
{
get
{
//check if nameProperty is null and return original display name value
if (_nameProperty == null)
{
return base.Description;
}
return (string)_nameProperty.GetValue(_nameProperty.DeclaringType, null);
}
}
}
public static class EnumExtender
{
public static string GetLocalizedDescription(this Enum #enum)
{
if (#enum == null)
return null;
string description = #enum.ToString();
FieldInfo fieldInfo = #enum.GetType().GetField(description);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return attributes[0].Description;
return description;
}
}
The Enum declaration
public enum MyEnum
{
[LocalizedEnum("ResourceName", NameResourceType = typeof(ResourceType))]
Test = 0
}
Then call MyEnumInstance.GetLocalizedDescription()
there is an easy solution:
use LocalizedDescription attribute to pass a resource key.
[Serializable]
public class LocalizableDescriptionAttribute:DescriptionAttribute
{
public LocalizableDescriptionAttribute(string resourceKey)
:base(Resources.ResourceManager.GetString(resourceKey))
{ }
}
One way I did it once, was to add an extention method in the same namespace as an enum, which returned a string. In my case it was just hardcoded, but would be no problem getting them from a resource file.
public static string Describe(this SomeEnum e)
{
switch(e)
{
SomeEnum.A:
return "Some text from resourcefile";
SomeEnum.B:
return "Some other text from resourcefile";
...:
return ...;
}
}
Maybe not an extremly smooth or fancy solution, but it works =)
Replace #nairik's method with the following to add support for flags enums.
public static string GetLocalizedDescription(this Enum #enum)
{
if ( #enum == null )
return null;
StringBuilder sbRet = new StringBuilder();
string description = #enum.ToString();
var fields = description.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach ( var field in fields )
{
FieldInfo fieldInfo = #enum.GetType().GetField(field);
DescriptionAttribute[] attributes = ( DescriptionAttribute[] )fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if ( attributes.Any() )
sbRet.AppendFormat("{0}, ", attributes[0].Description);
else
sbRet.AppendFormat("{0}, ", field);
}
if ( sbRet.Length > 2 )
sbRet.Remove(sbRet.Length - 2, 2);
return sbRet.ToString();
}
and replace NameResourceType in the attribute:
public Type NameResourceType
{
get
{
return _resourceType;
}
set
{
_resourceType = value;
_nameProperty = _resourceType.GetProperty(base.Description, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
}
}
See my table example in this question:
Localisation/I18n of database data in LINQ to SQL
The status type table maps to Enumeration values. The real benefit here is that you can have localisation in your reports and across your applications, and specify external IDs for integration with 3rd parties who don't want your internal values etc. It decouples the enum description from it's value.
You can't have multiple System.ComponentModel.DescriptionAttribute applied (so that option is out).
So add a level of indirection, the description holds a resource name, and then use the localisation support in resources. Clearly users of the enum will need to call your helper method to do this.
This looks good: http://www.codeproject.com/Articles/19980/Data-Binding-an-Enum-with-Descriptions
How do I find and replace a property using Linq in this specific scenario below:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
//TODO: Just copying values... Find out how to find the index and replace the value
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
Thanks for helping out in advance.
Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);
if (index != -1)
{
this.Properties[index] = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
Why are Array.Sort() and Array.IndexOf() methods static?
Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.
this.Properties[name] = value;
Note that neither solution is thread safe.
An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.
this.Properties = Enumerable.Union(
this.Properties.Where(p => p.Name != name),
Enumerable.Repeat(value, 1)).
ToArray();
[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(]
Is your 'Property' a class or a struct?
This test passes for me:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
[TestMethod]
public void TestMethod1()
{
var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
Assert.AreEqual("Y", pb["X"].Value);
pb["X"] = new Property { Name = "X", Value = "Z" };
Assert.AreEqual("Z", pb["X"].Value);
}
I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.