C# possible to use variable as property [duplicate] - c#

is there a way to get the value of a property of a object based on its name?
For example if I have:
public class Car : Vehicle
{
public string Make { get; set; }
}
and
var car = new Car { Make="Ford" };
I want to write a method where I can pass in the property name and it would return the property value. ie:
public string GetPropertyValue(string propertyName)
{
return the value of the property;
}

return car.GetType().GetProperty(propertyName).GetValue(car, null);

You'd have to use reflection
public object GetPropertyValue(object car, string propertyName)
{
return car.GetType().GetProperties()
.Single(pi => pi.Name == propertyName)
.GetValue(car, null);
}
If you want to be really fancy, you could make it an extension method:
public static object GetPropertyValue(this object car, string propertyName)
{
return car.GetType().GetProperties()
.Single(pi => pi.Name == propertyName)
.GetValue(car, null);
}
And then:
string makeValue = (string)car.GetPropertyValue("Make");

You want Reflection
Type t = typeof(Car);
PropertyInfo prop = t.GetProperty("Make");
if(null != prop)
return prop.GetValue(this, null);

Expanding on Adam Rackis's answer - we can make the extension method generic simply like this:
public static TResult GetPropertyValue<TResult>(this object t, string propertyName)
{
object val = t.GetType().GetProperties().Single(pi => pi.Name == propertyName).GetValue(t, null);
return (TResult)val;
}
You can throw some error handling around that too if you like.

In addition other guys answer, its Easy to get property value of any object by use Extension method like:
public static class Helper
{
public static object GetPropertyValue(this object T, string PropName)
{
return T.GetType().GetProperty(PropName) == null ? null : T.GetType().GetProperty(PropName).GetValue(T, null);
}
}
Usage is:
Car foo = new Car();
var balbal = foo.GetPropertyValue("Make");

Simple sample (without write reflection hard code in the client)
class Customer
{
public string CustomerName { get; set; }
public string Address { get; set; }
// approach here
public string GetPropertyValue(string propertyName)
{
try
{
return this.GetType().GetProperty(propertyName).GetValue(this, null) as string;
}
catch { return null; }
}
}
//use sample
static void Main(string[] args)
{
var customer = new Customer { CustomerName = "Harvey Triana", Address = "Something..." };
Console.WriteLine(customer.GetPropertyValue("CustomerName"));
}

To avoid reflection you could set up a Dictionary with your propery names as keys and functions in the dictionary value part that return the corresponding values from the properties that you request.

2 Very short options, 1 with a default value if it fails:
public object GetPropertyValue_WithDefault(
object _t,
string _prop,
object _default = null
)
{
PropertyInfo pi = _t.GetType().GetProperty(_prop);
return (pi == null
? _default
: pi.GetValue(_t, null)
);
}
public object GetPropertyValue(object _t, string _prop)
{
//because of "?." will return null if property not found
return _t.GetType().GetProperty(_prop)?.GetValue(_t, null);
}

Related

Custom attributes in c# for data types

I am thinking about making a custom attribute so that when we are using multiple data readers [SqldataReader] on different objects/tables, we could use the attribute to get the type of the property, and the "columnName" of the property. This way, we could then have a method that takes the data reader as a param, and from there could reflect the attributes to read in the columns. An example of what is currently being done is below, and then an example of what I am trying to accomplish. The problem I am having, is how to manage how to tell it what the (Type) is.
private static App GetAppInfo(SqlDataReader dr)
{
App app = new App();
app.ID = MCCDBUtility.GetDBValueInt(dr, "APPLICATION_ID");
app.Name = MCCDBUtility.GetDBValueString(dr, "APPNAME");
app.Desc = MCCDBUtility.GetDBValueString(dr, "APPDESCRIPTION");
app.Version = MCCDBUtility.GetDBValueString(dr, "APP_VERSION");
app.Type = MCCDBUtility.GetDBValueString(dr, "APPLICATIONTYPEID");
app.AreaName = MCCDBUtility.GetDBValueString(dr, "AREANAME");
return app;
}
What I am thinking though, so if I had a class for example like so:
[DataReaderHelper("MethodNameToGetType", "ColumnName")]
public string APPNAME {get;set;}
How could I go about this?
Fist of all, this is possible and if you like I could add a code sample.
But: This is not a good idea.
Why, you ask?
First - DataReader provides you with a method GetSchemaTable() which contains a property DataType which is a System.Type object. So basically you could create a MCCDBUtility.GetValue(dr, "columnName") that does the logic for your.
Second - What about you have a int property on your object but your datareader returns a decimal. For that case you can use Convert.ChangeType(value, type)
If you combine that you can achive what you want with
instance.Id = MCCDBUtility.GetValue<int>(dr, "columnName")
public T GetValue<T>(IDataReader reader, string columnName)
{
object value GetValue(reader, columnName);
return Convert.ChangeType(value, typeof(T));
}
private object GetValue(IDataReader reader, string columnName)
{
var schmema = reader.GetSchemaTable();
var dbType = typeof(object);
foreach(DataRowView row in schema.DefaultView)
if (row["columnName"].ToString().Equals(columnName, StringComparer.OrdinalIgnoreCase))
return row["ColumnType"];
if (dbType.Equals(typeof(int))
return GetInt(reader, columnName)
... // you get the point
else
return GetObject(reader, columnName);
}
And Third - Don't do this anyway there are great tools for mapping your query to your business objects. I don't want to name them all but a very lightweight and easy to understand is Dapper.NET, give it a try. https://github.com/StackExchange/dapper-dot-net
In combination with https://github.com/tmsmith/Dapper-Extensions you can easily map your database queries to your pocos
Update
As promised, here is the code for implementing on your own. Just create a Visual Studio Test project, insert the code and let it run. For readablity I omitted the unused IReadReader interface implementations, so you have to let intellisense create them for you.
Run the test and enjoy.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var values = new Dictionary<string, object>();
values.Add("ProductId", 17);
values.Add("ProductName", "Something");
values.Add("Price", 29.99M);
var reader = new FakeDataReader(values);
var product1 = new Product();
reader.SetValue(product1, p => p.Id);
reader.SetValue(product1, p => p.Name);
reader.SetValue(product1, p => p.Price);
Assert.AreEqual(17, product1.Id);
Assert.AreEqual("Something", product1.Name);
Assert.AreEqual(29.99M, product1.Price);
var product2 = new Product();
reader.SetAllValues(product2);
Assert.AreEqual(17, product2.Id);
Assert.AreEqual("Something", product2.Name);
Assert.AreEqual(29.99M, product2.Price);
}
}
public class Product
{
[Mapping("ProductId")]
public int Id { get; set; }
[Mapping("ProductName")]
public string Name { get; set; }
public decimal Price { get; set; }
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
public class MappingAttribute : Attribute
{
public MappingAttribute(string columnName)
{
this.ColumnName = columnName;
}
public string ColumnName { get; private set; }
}
public static class IDataReaderExtensions
{
public static void SetAllValues(this IDataReader reader, object source)
{
foreach (var prop in source.GetType().GetProperties())
{
SetValue(reader, source, prop);
}
}
public static void SetValue<T, P>(this IDataReader reader, T source, Expression<Func<T, P>> pe)
{
var property = (PropertyInfo)((MemberExpression)pe.Body).Member;
SetValue(reader, source, property);
}
private static void SetValue(IDataReader reader, object source, PropertyInfo property)
{
string propertyName = property.Name;
var columnName = propertyName;
var mapping = property.GetAttribute<MappingAttribute>();
if (mapping != null) columnName = mapping.ColumnName;
var value = reader.GetValue(reader.GetOrdinal(columnName));
var value2 = Convert.ChangeType(value, property.PropertyType);
property.SetValue(source, value2, null);
}
}
public static class ICustomFormatProviderExtensions
{
public static T GetAttribute<T>(this ICustomAttributeProvider provider)
{
return (T)provider.GetCustomAttributes(typeof(T), true).FirstOrDefault();
}
}
public class FakeDataReader : IDataReader
{
private Dictionary<string, object> values;
public FakeDataReader(Dictionary<string, object> values)
{
this.values = values;
}
public int GetOrdinal(string name)
{
int i = 0;
foreach (var key in values.Keys)
{
if (key.Equals(name, StringComparison.OrdinalIgnoreCase)) return i;
i++;
}
return -1;
}
public object GetValue(int i)
{
return values.Values.ToArray()[i];
}
}

Inheriting PropertyInfo

i wanted to extend the PropertyInfo class so that it could also contain the property value, without the need to have a reference to the original object as the following
public class PropertyInfoWithValue : PropertyInfo
{
private object value;
public object Value { get; set; }
public PropertyInfoWithValue(object value)
{
this.value = value;
}
}
but now the problem is i get couple exceptions that
`PropertyInfoWithValue does not implement the inherited abstract member System.Reflection.MemberInfo/PropertyInfo.XXXX`
is there a way that i could use the same implementations within
PropertyInfo?
the way i get property info
public static IEnumerable<PropertyInfoWithValue> GetColumns<T>(
this T obj, params Expression<Func<T, object>>[] lambda)
{
HashSet<string> set = new HashSet<string>(
lambda.Select(l => (l.Body as MemberExpression).Member as PropertyInfo)
.Select(x => x.Name)
);
if (set.Count == 0)
{
return obj.GetType().GetProperties().Select(p => new PropertyInfoWithValue(p.GetValue(obj, null))).ToList();
}
else
{
return obj.GetType().GetProperties().Where(p => set.Contains(p.Name)).Select(p => new PropertyInfoWithValue(p.GetValue(obj, null))).ToList();
}
}
You would have to need to implement all methods and properties that are marked abstract by the base class PropertyInfo, but I would advice creating a custom class that reflects the data that you are trying to retreive. If you still want to return PropertyInfo because you think you need it, then perhaps a wrapper class would be easier to implement and understand.
Example:
public class PropertyInfoWithValue
{
PropertyInfo propertyInfo;
public PropertyInfoWithValue(PropertyInfo propertyInfo, object value)
{
this.propertyInfo = propertyInfo;
SetValue(value);
}
public object Value { get; private set; }
public void SetValue(object value)
{
this.Value = value;
}
public static explicit operator PropertyInfoWithValue(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
return null;
// supply a default value, because we don't know it yet.
object value = null;
if (propertyInfo.PropertyType.IsValueType)
value = Activator.CreateInstance(propertyInfo.PropertyType);
return new PropertyInfoWithValue(propertyInfo, value);
}
public static explicit operator PropertyInfo(PropertyInfoWithValue
propertyInfoWithValue)
{
if (propertyInfoWithValue == null)
return null;
return propertyInfoWithValue.propertyInfo;
}
}
This way you would still be able to get the PropertyInfo by casting it back:
PropertyInfo propertyInfo = (PropertyInfo)myPropertyInfoWithValue;

C# Set Property in unknown object

I have to set a property inside an unknown object. The structure looks like this:
ObjA.ObjB().ObjC.PropA = propValue;
ObjA is from a referenced class. ObjB() is of type object and therefore ObjC is unknown. I thought about using Reflection but don't know how to use it correctly in this case.
object objB = ObjA.ObjB();
Type objBType = objB.GetType();
System.Reflection.XXXInfo objCInfo = objBType.GetXXX("ObjC");
Type objCType = objCInfo.GetType();
System.Reflection.PropertyInfo PropAInfo = objCType.GetProperty("PropA");
PropAInfo.SetValue(PropAInfo, propValue, null);
Answer (Thanks to BigM):
dynamic objAB = ObjA.ObjB();
objAB.ObjC.PropA = propValue;
This should probably work for you.
object objB = ObjA.ObjB();
Type objBType = objB.GetType();
System.Reflection.PropertyInfo objCInfo = objBType.GetProperty("ObjC");
object val = objCInfo.GetValue(objB);
Type objCType = val.GetType();
System.Reflection.PropertyInfo PropAInfo = objCType.GetProperty("PropA");
PropAInfo.SetValue(val, propValue, null);
However, I think a bit of re-architecting could be done here to make life a bit easier. For example, if you don't know anything about the types then you might consider using dynamic and returning dynamic types from ObjC and PropA - but there is a performance hit there.
On the other hand, if there is any way that you can use generics, that would make your life a lot easier. For example, the code here that sets the property value, if that method were generic it might likely be able to define the type of ObjC - but I can't really infer that with the current snippet.
Here are a couple of generic extension methods to help you get and set "unknown" properties by name:
public static class ReflectionHelpers
{
public static bool TrySetProperty<TValue>(this object obj, string propertyName, TValue value)
{
var property = obj.GetType()
.GetProperties()
.Where(p => p.CanWrite && p.PropertyType == typeof(TValue))
.FirstOrDefault(p => p.Name == propertyName);
if (property == null)
{
return false;
}
property.SetValue(obj, value);
return true;
}
public static bool TryGetPropertyValue<TProperty>(this object obj, string propertyName, out TProperty value)
{
var property = obj.GetType()
.GetProperties()
.Where(p => p.CanRead && p.PropertyType == typeof(TProperty))
.FirstOrDefault(p => p.Name == propertyName);
if (property == null)
{
value = default(TProperty);
return false;
}
value = (TProperty) property.GetValue(obj);
return true;
}
}
And a usage example:
public class Program
{
public static void Main()
{
var foo = new Foo
{
Bar = new Bar
{
HelloReflection = "Testing"
}
};
string currentValue;
if (foo.Bar.TryGetPropertyValue("HelloReflection", out currentValue))
{
Console.WriteLine(currentValue); // "Testing"
}
if (foo.Bar.TrySetProperty("HelloReflection", "123..."))
{
foo.Bar.TryGetPropertyValue("HelloReflection", out currentValue)
Console.WriteLine(currentValue); // "123.."
}
else
{
Console.WriteLine("Failed to set value");
}
}
}
public class Foo
{
public object Bar { get; set; }
}
public class Bar
{
public string HelloReflection { get; set; }
}

Reading attribute of a property using reflection

I need to read the attribute of a property using reflection
For example I get the following :
[XmlElement("Id")]
[CategoryAttribute("Main"), ReadOnly(true),
Description("This property is auto-generated")]
[RulesCriteria("ID")]
public override string Id
{
get { return _ID; }
set
{
_ID = value;
}
}
i want to get the " read only "value of this property using reflection
can anybody help
It's difficult to write the code for your case without knowing the Type name. Hope below example helps.
using System;
using System.Reflection;
public class Myproperty
{
private string caption = "Default caption";
public string Caption
{
get{return caption;}
set {if(caption!=value) {caption = value;}
}
}
}
class Mypropertyinfo
{
public static int Main(string[] args)
{
Console.WriteLine("\nReflection.PropertyInfo");
// Define a property.
Myproperty Myproperty = new Myproperty();
Console.Write("\nMyproperty.Caption = " + Myproperty.Caption);
// Get the type and PropertyInfo.
Type MyType = Type.GetType("Myproperty");
PropertyInfo Mypropertyinfo = MyType.GetProperty("Caption");
// Get and display the attributes property.
PropertyAttributes Myattributes = Mypropertyinfo.Attributes;
Console.Write("\nPropertyAttributes - " + Myattributes.ToString());
return 0;
}
}
MSDN
public static bool PropertyReadOnlyAttributeValue(PropertyInfo property)
{
ReadonlyAttribute attrib = Attribute.GetCustomAttribute(property, typeof(ReadOnlyAttribute));
return attrib != null && attrib.IsReadOnly;
}
public static bool PropertyReadOnlyAttributeValue(Type type, string propertyName)
{
return PropertyReadOnlyAttributeValue(type.GetProperty(propertyName));
}
public static bool PropertyReadOnlyAttributeValue(object instance, string propertyName)
{
if (instance != null)
{
Type type = instance.GetType();
return PropertyReadOnlyAttributeValue(type, propertyName);
}
return false;
}

Getting value (that may not exist) from within generic object

I need a list which can have its items queried for a particular property, and then returns the item if that property has the correct value. I came up with the following:
public class MyList<T>
{
public T[] items;
public Get( string name )
{
foreach( T item in items )
{
if( item.name == name )
return item;
}
return null; // if not found
}
}
The above gives a compile error because type T doesn't necessarily have the property that i'm checking. That makes sense, but what do I have to do to get this behaviour. Please note that I cannot use a Dictionary for reasons outside the scope of this question, although it is true that a Dictionary is essential what i'm trying to re-create.
Put a constraint behind your function definition
public class MyList<T> where T : YourObjectThatHasNameProperty
You could use Reflection like this:
public static Object TryGetPropertyValue(Object fromThis, String propertyName, Boolean isStatic)
{
// Get Type
Type baseType = fromThis.GetType();
// Get additional binding flags
BindingFlags addFlag = BindingFlags.Instance;
if(isStatic)
addFlag = BindingFlags.Static;
// Get PropertyInfo
PropertyInfo info = baseType.GetProperty(propertyName, BindingFlags.Public | addFlag);
// Check if we found the Property and if we can read it
if(info == null || !info.CanRead)
return null;
// Return the value
return info.GetValue(fromThis, null);
}
Edit: If the function return null, you can assume that the property does not exist on the provided Object.
You could use reflection to see if T has that property:
Type type = item.GetType();
bool hasproperty = type.GetProperties().Where(p => p.Name.Equals("name")).Any();
You need to constrain T to be of a type that has such a property:
interface INamed {
string Name { get; }
}
public class MyList<T> where T : INamed
public T[] items;
public T Get( string name ) {
foreach( T item in items ) {
if( item.Name == name )
return item;
}
return null; // if not found
}
}
Then, for instance,
class Foo : INamed {
private readonly string name;
private readonly int foo;
public string Name { get { return this.name; } }
public Foo(string name, int foo) {
this.name = name;
this.foo = foo;
}
}
MyList<Foo> list = // some instance of MyList<Foo>
Foo alice = list.Get("Alice");
Use a generic constraint.
public interface IHasName
{
string name;
}
public class MyList<T> where T : IHasName
{
public T[] items;
public Get( string name )
{
foreach( T item in items )
{
if( item.name == name )
return item;
}
return null; // if not found
}
}

Categories