Read override property attribute - c#

I have overridden property in class and would like to read custom attribute values, but it do not work. Could anyone explain why it is not working and how to solve the problem?
public class Validator
{
[Serializable]
public class CollectionAttribute : Attribute
{
public virtual string[] Data { get; set; }
public string Default;
}
}
class Helpers
{
public static MemberInfo GetMemberInfo<T, TU>(Expression<Func<T, TU>> expression)
{
var member = expression.Body as MemberExpression;
if (member != null)
return member.Member;
throw new ArgumentException("Expression is not a member access", "expression");
}
public static string GetName<T, TU>(Expression<Func<T, TU>> expression)
{
return GetMemberInfo(expression).Name;
}
public static string GetCollection<T, TU>(Expression<Func<T, TU>> expression)
{
var attribute = (Validator.CollectionAttribute[])GetMemberInfo(expression).GetCustomAttributes(typeof(Validator.CollectionAttribute), true);
return string.Join(",", attribute[0].Data);
}
}
Do not work.
public class TestClass:TestBaseClass
{
[Validator.Collection(Data = new[] { "doc", "docx", "dot", "dotx", "wpd", "wps", "wri" })]
public override string InputFormat { get; set; }
}
public class TestBaseClass
{
public virtual string InputFormat { get; set; }
}
Helpers.GetCollection((TestClass p) => p.InputFormat)
//The attribute variable in GetCollection method always null. It seems code looks for atribute in Base class.
Works fine.
public class TestClass
{
[Validator.Collection(Data = new[] { "doc", "docx", "dot", "dotx", "wpd", "wps", "wri" })]
public override string InputFormat { get; set; }
}
Helpers.GetCollection((TestClass p) => p.InputFormat)

The declaring type of InputFormat is TestBaseClass which doesn't have that attribute. And the PropertyInfo which returned is for the declaring type, not for the actual type of the parameter.
What you have to do, is retrieve the actual type of the expression's parameter, and then return the PropertyInfo for that type.
public static MemberInfo GetMemberInfo<T, TU>(Expression<Func<T, TU>> expression)
{
var member = expression.Body as MemberExpression;
if (member != null)
{
// Getting the parameter's actual type, and retrieving the PropertyInfo for that type.
return expression.Parameters.First().Type.GetProperty(member.Member.Name);
}
throw new ArgumentException("Expression is not a member access", "expression");
}

Related

Unable to cast object of type 'System.Linq.Expressions.ConstantExpression

I try to run this code fragment.
But I can this error:
$exception {"Unable to cast object of type 'System.Linq.Expressions.ConstantExpression' to type 'System.Linq.Expressions.MemberExpression'."} System.InvalidCastException
on this line:
fields.Add(((MemberExpression)ue.Operand).Member.Name.ToLower());
class Program
{
public static string CreateUrl(string url, params Expression<Func<User, object>>[] fieldSelectors)
{
var fields = new List<string>();
foreach (var selector in fieldSelectors)
{
var body = selector.Body;
if (body is MemberExpression me)
{
fields.Add(me.Member.Name.ToLower());
}
else if (body is UnaryExpression ue)
{
fields.Add(((MemberExpression)ue.Operand).Member.Name.ToLower());
}
}
var selectedFields = string.Join(',', fields);
return string.Concat(url, "?fields=", selectedFields);
}
public static string CreateUrl(string url, params string[] fields)
{
var selectedFields = string.Join(',', fields);
return string.Concat(url, "?fields=", selectedFields);
}
public static void Main(string[] args)
{
string url = "http://www.exmaple.com/users";
CreateUrl(url, url => url.Name, url => 1).Dump("1");
CreateUrl(url, "Name", "age").Dump("2");
Console.ReadKey();
}
}
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
So what I have to correct?
Thank you
When I glance at their class definitions, I don't see any direct relation enabling you being able to do the cast.
public class MemberExpression : System.Linq.Expressions.Expression
public sealed class UnaryExpression : System.Linq.Expressions.Expression
Oh my... I had a typo:
This is the correct way to do it:
CreateUrl(url, u => u.Name, u => u.Age).Dump("1");
CreateUrl(url, "Name", "Age").Dump("2");

A variable of two types

I need to read an JSON object from TypeScript, which has a variable names prop which of two types, either Identifier or Expression to C#. TypeScript can have a variable with multiple types (with its union type feature), e.g., prop is defined as
var property:Identifier | Expression
I am reading the JSON object string from C# with JsonConvert.DeserializeObject, e.g.,
Object facebookFriends = new JavaScriptSerializer().Deserialize<Object>(JSON_Object);
How should I declare a variable of two types in C#? Or should I just use object (i.e., declare as Object property)?
If the property can have either of these two formats:
{ "prop": "identifier" }
{ "prop": { "complex": "object" } }
ie. either a simple string or a more complex object you can solve this by using dynamic to accept the parsed prop value, and extra properties to return either the identifier or the complex object. Here is a LINQPad program that demonstrates:
void Main()
{
var jsonStrings = new[]
{
"{ \"prop\": \"identifier\" }",
"{ \"prop\": { \"complex\": \"object\" } }"
};
jsonStrings.Select(json => JsonConvert.DeserializeObject<Test>(json)).Dump();
}
public class Test
{
public Test()
{
_Complex = new Lazy<Complex>(GetComplex);
}
[JsonProperty("prop")]
public dynamic prop { get; set; }
[JsonIgnore]
public string Identifier => prop as string;
private readonly Lazy<Complex> _Complex;
[JsonIgnore]
public Complex Complex => _Complex.Value;
private Complex GetComplex()
{
if (!(prop is JObject))
return null;
return ((JObject)prop).ToObject<Complex>();
}
}
public class Complex
{
public string complex { get; set; }
}
Output:
A different approach would be to create your own types to handle the multiple outputs and again use a secondary property to evaluate what you actually got from the json:
void Main()
{
var jsonStrings = new[]
{
"{ \"prop\": \"identifier\" }",
"{ \"prop\": { \"complex\": \"object\" } }"
};
jsonStrings.Select(json => JsonConvert.DeserializeObject<Test>(json)).Dump();
}
public class Test
{
[JsonProperty("prop")]
public dynamic prop { get; set; }
[JsonIgnore]
public PropertyValue Property
{
get
{
if (prop is string)
return new IdentifierProperty(prop as string);
return new ExpressionProperty(((JObject)prop).ToObject<Expression>());
}
}
}
public abstract class PropertyValue
{
}
public class IdentifierProperty : PropertyValue
{
public IdentifierProperty(string identifier)
{
Identifier = identifier;
}
public string Identifier { get; }
public override string ToString() => Identifier;
}
public class ExpressionProperty : PropertyValue
{
public ExpressionProperty(Expression expression)
{
Expression = expression;
}
public Expression Expression { get; }
public override string ToString() => Expression.ToString();
}
public class Expression
{
public string complex { get; set; }
public override string ToString() => $"complex: {complex}";
}
Output:

Get Custom Attributes Generic

I am creating a custom attribute. And I will use it in multiple classes:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class Display : System.Attribute
{
public string Name { get; set; }
public string Internal { get; set; }
}
public class Class1
{
[Display(Name = "ID")]
public int ID { get; set; }
[Display(Name = "Name")]
public string Title { get; set; }
}
public class Class2
{
[Display(Name = "ID")]
public int ID { get; set; }
[Display(Name = "Name")]
public string Title { get; set; }
}
Here is working correctly but I want to make it as generic as possible, like the MVC example:
Class1 class1 = new Class1();
class1.Title.DisplayName () / / returns the value "Name"
The only thing I could do was generate a loop of my property, but I need to inform my typeof Class1
foreach (var prop in typeof(Class1).GetProperties())
{
var attrs = (Display[])prop.GetCustomAttributes(typeof(Display), false);
foreach (var attr in attrs)
{
Console.WriteLine("{0}: {1}", prop.Name, attr.Name);
}
}
Is there any way to do it the way I want?
You can't do what you've shown exactly because class1.Title is an expression that evaluates to a string. You are looking for metadata about the Title property. You can use Expression Trees to let you write something close. Here's a short helper class that pulls the attribute value out of a property in an expression tree:
public static class PropertyHelper
{
public static string GetDisplayName<T>(Expression<Func<T, object>> propertyExpression)
{
Expression expression;
if (propertyExpression.Body.NodeType == ExpressionType.Convert)
{
expression = ((UnaryExpression)propertyExpression.Body).Operand;
}
else
{
expression = propertyExpression.Body;
}
if (expression.NodeType != ExpressionType.MemberAccess)
{
throw new ArgumentException("Must be a property expression.", "propertyExpression");
}
var me = (MemberExpression)expression;
var member = me.Member;
var att = member.GetCustomAttributes(typeof(DisplayAttribute), false).OfType<DisplayAttribute>().FirstOrDefault();
if (att != null)
{
return att.Name;
}
else
{
// No attribute found, just use the actual name.
return member.Name;
}
}
public static string GetDisplayName<T>(this T target, Expression<Func<T, object>> propertyExpression)
{
return GetDisplayName<T>(propertyExpression);
}
}
And here's some sample usage. Note that you really don't even need an instance to get this metadata, but I have included an extension method that may be convenient.
public static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.WriteLine(class1.GetDisplayName(c => c.Title));
Console.WriteLine(PropertyHelper.GetDisplayName<Class1>(c => c.Title));
}

GetCustomAttributes does not return value

I have defined a custom attribute
[AttributeUsage(AttributeTargets.Property )]
public class FieldAttribute: Attribute
{
public FieldAttribute(string field)
{
this.field = field;
}
private string field;
public string Field
{
get { return field; }
}
}
My Class which uses the custom attribute is as below
[Serializable()]
public class DocumentMaster : DomainBase
{
[Field("DOC_CODE")]
public string DocumentCode { get; set; }
[Field("DOC_NAME")]
public string DocumentName { get; set; }
[Field("REMARKS")]
public string Remarks { get; set; }
}
}
But when i try to get the value of the custom attribute it returns null object
Type typeOfT = typeof(T);
T obj = Activator.CreateInstance<T>();
PropertyInfo[] propInfo = typeOfT.GetProperties();
foreach (PropertyInfo property in propInfo)
{
object[] colName = roperty.GetCustomAttributes(typeof(FieldAttributes), false);
/// colName has no values.
}
What am I missing?
typo: should be typeof(FieldAttribute). There is an unrelated system enum called FieldAttributes (in System.Reflection, which you have in your using directives, since PropertyInfo resolves correctly).
Also, this is easier usage (assuming the attribute doesn't allow duplicates):
var field = (FieldAttribute) Attribute.GetCustomAttribute(
property, typeof (FieldAttribute), false);
if(field != null) {
Console.WriteLine(field.Field);
}

c# - How to iterate through classes fields and set properties

I am not sure if this is possible but I want to iterate through a class and set a field member property without referring to the field object explicitly:
public class Employee
{
public Person _person = new Person();
public void DynamicallySetPersonProperty()
{
MemberInfo[] members = this.GetType().GetMembers();
foreach (MemberInfo member in members.Where(a => a.Name == "_person"))
//get the _person field
{
Type type = member.GetType();
PropertyInfo prop = type.GetProperty("Name"); //good, this works, now to set a value for it
//this line does not work - the error is "property set method not found"
prop.SetValue(member, "new name", null);
}
}
}
public class Person
{
public string Name { get; set; }
}
In the answer that I marked as the answer you need to add:
public static bool IsNullOrEmpty(this string source)
{
return (source == null || source.Length > 0) ? true : false;
}
Here's a complete working example:
public class Person
{
public string Name { get; set; }
}
class Program
{
static void PropertySet(object p, string propName, object value)
{
Type t = p.GetType();
PropertyInfo info = t.GetProperty(propName);
if (info == null)
return;
if (!info.CanWrite)
return;
info.SetValue(p, value, null);
}
static void PropertySetLooping(object p, string propName, object value)
{
Type t = p.GetType();
foreach (PropertyInfo info in t.GetProperties())
{
if (info.Name == propName && info.CanWrite)
{
info.SetValue(p, value, null);
}
}
}
static void Main(string[] args)
{
Person p = new Person();
PropertySet(p, "Name", "Michael Ellis");
Console.WriteLine(p.Name);
PropertySetLooping(p, "Name", "Nigel Mellish");
Console.WriteLine(p.Name);
}
}
EDIT: added a looping variant so you could see how to loop through property info objects.
public class Person
{
public string Name { get; set; }
}
public class Employee
{
public Person person = new Person();
public void DynamicallySetPersonProperty()
{
var p = GetType().GetField("person").GetValue(this);
p.GetType().GetProperty("Name").SetValue(p, "new name", null);
}
}
With the following Extension methods that I have created, you can set or get any property value even if they are nested
GetPropertyValue(customObject, "Property.Nested.Child.Name");
or set
SetPropertyValue(customObject, "Property.Nested.Child.Name", "my custom name");
private class TargetProperty
{
public object Target { get; set; }
public PropertyInfo Property { get; set; }
public bool IsValid { get { return Target != null && Property != null; } }
}
private static TargetProperty GetTargetProperty(object source, string propertyName)
{
if (!propertyName.Contains("."))
return new TargetProperty { Target = source, Property = source.GetType().GetProperty(propertyName) };
string[] propertyPath = propertyName.Split('.');
var targetProperty = new TargetProperty();
targetProperty.Target = source;
targetProperty.Property = source.GetType().GetProperty(propertyPath[0]);
for (int propertyIndex = 1; propertyIndex < propertyPath.Length; propertyIndex++)
{
propertyName = propertyPath[propertyIndex];
if (!string.IsNullOrEmpty(propertyName))
{
targetProperty.Target = targetProperty.Property.GetValue(targetProperty.Target, null);
targetProperty.Property = targetProperty.Target.GetType().GetProperty(propertyName);
}
}
return targetProperty;
}
public static bool HasProperty(this object source, string propertyName)
{
return GetTargetProperty(source, propertyName).Property != null;
}
public static object GetPropertyValue(this object source, string propertyName)
{
var targetProperty = GetTargetProperty(source, propertyName);
if (targetProperty.IsValid)
{
return targetProperty.Property.GetValue(targetProperty.Target, null);
}
return null;
}
public static void SetPropertyValue(this object source, string propertyName, object value)
{
var targetProperty = GetTargetProperty(source, propertyName);
if(targetProperty.IsValid)
{
targetProperty.Property.SetValue(targetProperty.Target, value, null);
}
}
And here are a couple of tests for it
[TestFixture]
public class ObjectExtensionsTest
{
private class MockClass
{
public MockClass()
{
Nested = new NestedMockClass();
}
public string Id { get; set; }
public string Name { get; set; }
public string GetOnly { get { return "MockClass"; } }
public string SetOnly { set { } }
public NestedMockClass Nested { get; set; }
}
private class NestedMockClass
{
public string NestedId { get; set; }
public string NestedName { get; set; }
public string NestedGetOnly { get { return "NestedMockClass"; } }
public string NestedSetOnly { set { } }
}
[Test]
public void TestShouldFindProperty()
{
MockClass mockObject = new MockClass();
Assert.IsTrue(mockObject.HasProperty("Id"));
Assert.IsTrue(mockObject.HasProperty("Name"));
Assert.IsTrue(mockObject.HasProperty("GetOnly"));
Assert.IsTrue(mockObject.HasProperty("SetOnly"));
Assert.IsTrue(mockObject.HasProperty("Nested"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedId"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedName"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedGetOnly"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedSetOnly"));
}
[Test]
public void TestShouldGetPropertyValue()
{
MockClass mockObject = new MockClass();
mockObject.Id = "1";
mockObject.Name = "Name";
mockObject.Nested.NestedId = "NestedId";
mockObject.Nested.NestedName = "NestedName";
Assert.AreEqual(mockObject.Id, mockObject.GetPropertyValue("Id"));
Assert.AreEqual(mockObject.Name, mockObject.GetPropertyValue("Name"));
Assert.AreEqual(mockObject.GetOnly, mockObject.GetPropertyValue("GetOnly"));
Assert.AreEqual(mockObject.Nested.NestedId, mockObject.GetPropertyValue("Nested.NestedId"));
Assert.AreEqual(mockObject.Nested.NestedName, mockObject.GetPropertyValue("Nested.NestedName"));
}
[Test]
public void TestShouldSetPropertyValue()
{
MockClass mockObject = new MockClass();
mockObject.SetPropertyValue("Id", "1");
mockObject.SetPropertyValue("Name", "Name");
mockObject.SetPropertyValue("Nested.NestedId", "NestedId");
mockObject.SetPropertyValue("Nested.NestedName", "NestedName");
Assert.AreEqual(mockObject.Id, "1");
Assert.AreEqual(mockObject.Name, "Name");
Assert.AreEqual(mockObject.Nested.NestedId, "NestedId");
Assert.AreEqual(mockObject.Nested.NestedName, "NestedName");
}
}
Hope you find it useful.
You are trying to set the Name property of your Employee class's _person field. It doesn't have one. Try this:
prop.SetValue(((FieldInfo)member).GetValue(this), "new name", null)
Not sure if you need to cast the first argument like this:
prop.SetValue((Person)((FieldInfo)member).GetValue(this), "new name", null)
This then applies it to the value of the _person field instead.
You a trying to perform SetValue() on the property Name of the variable member that is a MemberInfo object and this proeprty is read only.
Note you do not need to iterate over all memebers and you do not need to get the field _person with reflection as it is defined in the same class as the method DynamicallySetPersonProperty().
So the code shoul read like this.
PropertyInfo property = this._person.GetType().GetProperty("Name");
property.SetValue(this._person, "new name", null);
The first line will fail if _person is null. So you can use reflectiopn to get the type of the field.
FieldInfo field = this.GetType().GetField("_person", BindingFlags.Public);
PropertyInfo property = field.FieldType.GetProperty("Name");
But now accessing this property will still fail if _personis null.
property.Setvalue(field.GetValue(this), "new name", null);
try this:
public static void ApplyPropertyChanges(this object objDest, object objToCopyFrom)
{
if (objDest == null)
throw new ArgumentNullException();
if (objToCopyFrom == null)
throw new ArgumentNullException("objToCopyFrom");
if (objDest.GetType() != objToCopyFrom.GetType())
throw new Exception("Invalid type. Required: \"" + objDest.GetType().ToString() + "\"");
foreach (System.Reflection.PropertyInfo piOrig in objDest.GetType().GetProperties())
{
object editedVal = objToCopyFrom.GetType().GetProperty(piOrig.Name).GetValue(objToCopyFrom, null);
piOrig.SetValue(objDest,
editedVal,
null);
}
}
usage example:
public ActionResult Edit(Team editedTeamData)
{
if (!ModelState.IsValid)
return View();
Team origTeam = (from t in _db.Teams
where t.TeamID == editedTeamData.TeamID
select t).FirstOrDefault();
origTeam.ApplyPropertyChanges(editedTeamData);
_db.SubmitChanges();
return RedirectToAction("Index");
}

Categories

Resources