In a base class I have the following method for derived classes:
protected virtual void SetValue<T>(ref T field, string propertyName, T value)
{
//assign value to the field and do some other staff
...
}
Is there any way to check if fieldVar has an attribute applied (for example DataMemberAttribute)?
No, there is no way to do that, except that it looks like you're also told the property name.
If you can find the FieldInfo of the field, then you can find any attributes, but not through the ref-parameter alone.
Reading between the lines you have a set of private fields which back the values of public properties. On some or all of these properties you some data attributes attached that you want to discover.
PropertyInfo pi = this.GetType().GetProperty(propertyName);
object[] dataMemberAttributes = pi.GetCustomAttributes(typeof(DataMemberAttribute, true);
if (dataMemberAttributes.Length > 0)
{
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)dataMemberAttributes[0];
// Do stuff with the attribute.
}
Related
To simplify the case, let say I have the following class
public class TestFileHelpersClass
{
[FieldOrder(1), FieldFixedLength(20), FieldTrim(TrimMode.Both)]
public string Field1 { get; set; }
[FieldOrder(2), FieldFixedLength(20), FieldTrim(TrimMode.Both)]
private string Field2 { get; set; }
// If change to a normal variable, there is no exception and FileHelper can work perfectly.
// private string Field2;
public TestFileHelpersClass()
{
this.Field1 = "Field1 Value";
this.Field2 = "Field2 Value";
}
}
Then, there is an exception thrown when I instantiate the FileHelperEngine.
static void TestFileHelpers()
{
// FileHelpers.BadUsageException: 'The field: '<Field2>k__BackingField' must be marked with the FieldFixedLength attribute because the record class is marked with FixedLengthRecord.'
FileHelperEngine<TestFileHelpersClass> engine = new FileHelperEngine<TestFileHelpersClass>();
TestFileHelpersClass a = new TestFileHelpersClass();
string result = engine.WriteString(new List<TestFileHelpersClass> { a });
}
But {Field2} has already been marked with {FieldFixedLength(20)}
If I change {Field2} from a property to variable, it is working fine.
The question is:
When it is public, FileHelpers works perfectly in both the variable and property case.
When it is private, variable is still ok, but property fails to work.
Any idea why private property and variable behave differently in FileHelpers?
When you write autoprops, the compiler generates backing fields for you:
FileHelpers has ReflectionHelper.cs that picks up the fields in a type you want it to parse:
This means, for my dumb class in the first screenshot, it will pick up the two __BackingField, and the two normally named fields:
When FileHelpers is processing these in FieldBase.cs, for any backing fields that have a friendly name, it goes for the property:
fi is the __BackingField; it does have a friendly name because it's come from PrivateProp. To go for the property, FH calls this:
var prop = fi.DeclaringType.GetProperty(fieldFriendlyName);
This call won't discover a private property because it doesn't specify any BindingFlags.NonPublic|BindingFlags.Instance, so prop ends up null and FH doesn't switch to looking at the property
Look back at the first image; the FileHelper custom attributes are on the property, not the backing field. Later in this same FieldBase.cs class, FH tries to find derivates of its own attributes (FH's attributes derive from FieldAttribute) on the member being inspected:
The member being inspected is the __BackingField, which doesn't have any FH attributes; it's the prop that has the attributes. The field does have attributes (CompilerGenerated, DebuggerBrowsable) but because this call looks only for FH's attributes and the backing field doesn't have any FH custom attributes it is essentially looking on the wrong thing. It reaches a situation where it cannot work - it needs attributes you've put on the prop, but it looks on the field, finds no FH attribs and throws an error because it needs those attributes it can never find.
If fi.DeclaringType.GetProperty(fieldFriendlyName) had instead used an overload that looked for private props e.g. fi.DeclaringType.GetProperty(fieldFriendlyName, BindingFlags.NonPublic|BindingFlags.Instance|..other_binding_flags_here) then it would have found the private prop you decorated (but I don't guarantee that this is a bug or that that is the resolution)
You could report a bug and see if the author agrees, you could amend the library yourself and use the amended version, or you could just use fields/public props instead of private props
I cannot answer why the library was coded this way; few people here can. Questions of the ilk "what was developer X thinking when..." are seldom a good fit for SO.
If I have:
[SomeAttr]
public Int32 SomeProperty
{
get; set;
}
Is it possible for SomeAttr to tell what property it's tacked onto? Is it atleast possible to tell what Type the property is?
No. You can only do it the other way around. You can query the property (through reflection) what attributes it has:
Attribute.GetCustomAttributes(prop, true);
Where prop is an object derived from the MemberInfo class that describes the property SomeProperty. Next you iterate through the returned attributes to see if it includes yours.
No, you can't. Not directly.
What you can do, at the moment you collect the attribute, is set that attribute with extra information:
class SomeAttr: Attribute
{
public PropertyInfo Target {get;set;}
}
... and when you have collected the information:
Type type = ... // The type which holds the property.
PropertyInfo propertyInfo = typeo.GetProperty("SomeProperty");
Type propertyType = propertyInfo.PropertyType;
SomeAttr attr = propertyInfo.GetCustomAttributes(false).OfType<SomeAttr>().FirstOrDefault();
attr.Target = propertyInfo; // <== Set the target information.
This way, you can always retrieve its target member at another point in your code:
public void DoSomethingWithAttribute(SomeAttr attr)
{
PropertyInfo whichProperty = attr.Target;
}
(You can also use the baseclass MemberInfo, to support methods, fields, etc.)
I have a set of user controls on a page that are dynamically loaded based on condition to run a variety reports (the condition driver). Each control has one or more properties exposed that will be used to get data from my database query. Because the controls vary for each report I wrote a procedure to access the appropriate control's property by name so I can send it to the database query in the code behind (C#). I got it all setup to access the public property like this:
stringVal = userControl.Attributes[stringName].ToString();
and it is telling me that I need to new up an object. I don't understand how I need to access that property dynamically by string name. In my immediate window I can see the property I want; but, it is not an "Attribute" as control.Attributes.Count = 0. So, how do I need to set this up properly so I can access it by string name? Do I need to decorate the property with something?
Thank you in advance.
Below, I've written a wrapper class you can use to set/get public fields or properties of the wrapped class by string name.
First, let's look at how you could use it on a class that had the public fields or properties StartDate and SocialSecurityNumber.
// First create the instance we'll want to play with
MyUserControl myControlInstance = new MyUsercontrol();
// MyUserContol has two properties we'll be using
// DateTime StartDate
// int SocialSecurityNumber
// Now we're creating a map to facilitate access to the properties
// of "myControlInstance" using strings
PropertyMap<MyUserControl> map =
new PropertyMap<MyUserControl>(myControlInstance);
// Since the map is directed toward "myControlInstance"
// this line is equivalent to:
// myControlInstance.StartDate = Datetime.Now;
map.Set<DateTime>("StartDate", DateTime.Now);
// This line is equivalent to:
// ssn = myUsercontrol.SocialSecurityNumber;
int ssn = map.Get<int>("SocialSecurityNumber");
And now on to how it's implemented:
public class PropertyMap<T>
{
readonly T Instance;
public PropertyMap(T instance)
{
Instance = instance;
}
public U Get<U>(string PropertyName)
{
// Search through the type's properties for one with this name
// Properties are things with get/set accessors
PropertyInfo property = typeof(T).GetProperty(PropertyName);
if (property == null)
{
// if we couldn't find a property, look for a field.
// Fields are just member variables, but you can only
// manipulate public ones like this.
FieldInfo field = typeof(T).GetField(PropertyName);
if (field == null)
throw new Exception("Couldn't find a property/field named " + PropertyName);
return (U)field.GetValue(Instance);
}
return (U)property.GetValue(Instance, null);
}
public void Set<U>(string PropertyName, U value)
{
// Search through the type's properties for one with this name
PropertyInfo property = typeof(T).GetProperty(PropertyName);
if (property == null)
{
// if we couldn't find a property, look for a field.
FieldInfo field = typeof(T).GetField(PropertyName);
if (field == null)
throw new Exception("Couldn't find a property/field named " + PropertyName);
field.SetValue(Instance, value);
return;
}
property.SetValue(Instance, value, null);
}
}
You need to explore reflection. It relates to manipulating metadata of .NET types (classes, structs, enums, etc). However, if you're running your app in shared hosting with partial trust, you may not be able to have any kind of reflection code run at all (security restriction on the server). If this is the case, test on a small/quick example first (on your hosting), and then make decisions appropriately: whether to redesign your app or change hosts.
A reflection-related snippet that might be useful to you (paste this somewhere within the user control class, as this.GetType() matters:
var properties = this.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (var p in properties)
{
string propertyName = p.Name;
}
There are other ways to get the type; e.g. typeof(yourclassname); you can also reflect/get all types from given assembly (dll), and much more.
my class property has default value which will be serialize.
public class DeclaredValue
{
[XmlElement(ElementName = "Amount", DataType = "double", IsNullable = false), DefaultValue(999)]
public double Amount { get; set; }
[XmlElement(ElementName = "Reference2", DataType = "string", IsNullable = false), DefaultValue("")]
public string Reference2 { get; set; }
}
so we create instance of DeclaredValue class and provide value for Reference2 property and do not assign anything for Amount. so when we serialize the class DeclaredValue then no tag found for amount in my xml. i mention default value for amount "999" then why it does not work in serialization. i want that if do not assign anything for amount then amoun tag should be there in my xml with default value.
to do this what way i need to decorate the amount property that it always comes with default value in xml after serialization if user do not assign anything to this property.
please guide me what i need to change in the code to get my desired output.
Per the note on MSDN:
A DefaultValueAttribute will not cause
a member to be automatically
initialized with the attribute's
value. You must set the initial value
in your code.
Somewhat surprisingly the DefaultValue only regulates the writing of an object, members that are equal to their DefaultValue will not be written out.
You must still initialize members before or after loading yourself, for example in the constructor.
Let me thoroughly describe what is happening.
When XmlSerializer Deserialize() method is called, it creates a new object using a default constructor. It doesn't apply any DefaultValueAttributes to this object, I beleave, because of assumption that default ctor should "know better" how to initialize values by default. From this point of view - it is logical.
XmlSerializer doesn't serialize members which values are the same as marked by DefaultValue attribute. From some point of view such behavior is driven by logic too.
But when you do not initialize members in ctor and call deserialize method, XmlSerializer see no corresponding xml field, but it see that the field/property has DefaultValueAttribute, serializer just leave such value (according to the assumption that the default constructor knows better how to initialize a class "by defaults"). And you've got your zeros.
Solution
To initialize a class members by these DefaultValueAttributes (sometimes it is very handy to have this initialization values just in place) you can use such simple method:
public YourConstructor()
{
LoadDefaults();
}
public void LoadDefaults()
{
//Iterate through properties
foreach (var property in GetType().GetProperties())
{
//Iterate through attributes of this property
foreach (Attribute attr in property.GetCustomAttributes(true))
{
//does this property have [DefaultValueAttribute]?
if (attr is DefaultValueAttribute)
{
//So lets try to load default value to the property
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
try
{
//Is it an array?
if (property.PropertyType.IsArray)
{
//Use set value for arrays
property.SetValue(this, null, (object[])dv.Value);
}
else
{
//Use set value for.. not arrays
property.SetValue(this, dv.Value, null);
}
}
catch (Exception ex)
{
//eat it... Or maybe Debug.Writeline(ex);
}
}
}
}
}
This "public void LoadDefaults()", can be decorated as an Extension to object or use as some static method of a helper class.
As Henk Holterman mentionned, this attribut doesn't set the default value automatically. Its purpose is mostly to be used by visual designers to reset a property to its default value.
As others mentioned, the DefaultValue attribute doesn't initialize the property. You could use a simple loop to set all properties:
foreach (var property in GetType().GetProperties())
property.SetValue(this, ((DefaultValueAttribute)Attribute.GetCustomAttribute(
property, typeof(DefaultValueAttribute)))?.Value, null);
Even though ?.Value could return null, it works with non-nullable types, I tested this.
If only few of your properties have a default value, you should maybe only set the value if it is there.
If all properties should have a default value, remove the ? to get an error if you forgot one.
Most likely, arrays won't work, see MajesticRa's solution how to handle that.
Taking a look at the following question, Real world use of custom .NET attributes how would you implement the solution proposed by #Esteban?
I'm a little confused as to when and where the code would get executed I think. Could you please supply a good sample of code.
I've asked this question before but didn't properly phrase it I think so...
With respect to the question/answer that you reference, I assume that there would be some code that runs either in the data layer or in the class itself that does validation. That code would use Reflection on the object being validated to find the properties with different attributes and run the specific validation logic associated with that attribute on that property.
It might look something like the following:
public void Validate( object obj )
{
foreach (var property in obj.GetType().GetProperties())
{
var attribute = property.GetCustomAttributes(typeof(ValidationAttribute), false);
var validator = ValidationFactory.GetValidator( attribute );
validator.Validate( property.GetValue( obj, null ) );
}
}
On submit(save) of the html form(win form) you get back changed Customer class. For each property you check if it has custom attribute(inherited from ValidationAttribute or implementing IValiador interface or something like this) associated with it. For each such property you call the validate method of attribute(create appropriate validation class and call validate method) on the property value.
You would use reflection:
public class MyClass
{
[Description("I'm an attribute!")]
public int MyField;
public Attribute GetAttribute(string fieldName)
{
FieldInfo field = typeof(MyClass).GetField("MyField");
Attribute[] attributes = (Attribute[])field.GetCustomAttributes(typeof(Attribute), false);
DescriptionAttribute desc = (DescriptionAttribute)attributes[0];
return desc;
}
}
If the attributed member is a field, you would use FieldInfo, as used in the example. If it's a property, you would use PropertyInfo, the members of FieldInfo and PropertyInfo are pretty much the same.