Get the field name from the parameter [duplicate] - c#

I want to get the value of a field of an object by using a string as variable name.
I tried to do this with reflection:
myobject.GetType().GetProperty("Propertyname").GetValue(myobject, null);
This works perfectly but now I want to get the value of "sub-properties":
public class TestClass1
{
public string Name { get; set; }
public TestClass2 SubProperty = new TestClass2();
}
public class TestClass2
{
public string Address { get; set; }
}
Here I want to get the value Address from a object of TestClass1.

You already did everything you need to do, you just have to do it twice:
TestClass1 myobject = ...;
// get SubProperty from TestClass1
TestClass2 subproperty = (TestClass2) myobject.GetType()
.GetProperty("SubProperty")
.GetValue(myobject, null);
// get Address from TestClass2
string address = (string) subproperty.GetType()
.GetProperty("Address")
.GetValue(subproperty, null);

Your SubProperty member is actually a Field and not a Property, that is why you can not access it by using the GetProperty(string) method. In your current scenario, you should use the following class to first get the SubProperty field, and then the Address property.
This class will allow you to specify the return type of your property by closing the type T with the appropriate type. Then you will simply need to add to the first parameter the object whose members you are extracting. The second parameter is the name of the field you are extracting while the third parameter is the name of the property whose value you are trying to get.
class SubMember<T>
{
public T Value { get; set; }
public SubMember(object source, string field, string property)
{
var fieldValue = source.GetType()
.GetField(field)
.GetValue(source);
Value = (T)fieldValue.GetType()
.GetProperty(property)
.GetValue(fieldValue, null);
}
}
In order to get the desired value in your context, simply execute the following lines of code.
class Program
{
static void Main()
{
var t1 = new TestClass1();
Console.WriteLine(new SubMember<string>(t1, "SubProperty", "Address").Value);
}
}
This will give you the value contained in the Address property. Just make sure you first add a value to the said property.
But should you actually want to change the field of your class into a property, then you should make the following change to the original SubMember class.
class SubMemberModified<T>
{
public T Value { get; set; }
public SubMemberModified(object source, string property1, string property2)
{
var propertyValue = source.GetType()
.GetProperty(property1)
.GetValue(source, null);
Value = (T)propertyValue.GetType()
.GetProperty(property2)
.GetValue(propertyValue, null);
}
}
This class will now allow you to extract the property from your initial class, and get the value from the second property, which is extracted from the first property.

try
myobject.GetType().GetProperty("SubProperty").GetValue(myobject, null)
.GetType().GetProperty("Address")
.GetValue(myobject.GetType().GetProperty("SubProperty").GetValue(myobject, null), null);

Related

Set Default value for missing Complex properties with JSON.net (JsonConvert.SerializeObject or JsonConvert.DeSerializeObject)

I have a requirement where I need to set default value to the below complex property Instances using JsonProperty and DefaultValue.
I know we can achieve this for primitive properties as mentioned in the below link, but need to know how we can do it for complex properties.
Default value for missing properties with JSON.net
Below is the default Instances value I need to set using DefaultValue(). Please let me know how to achieve this.
Default value to be set to Instances property:
Instance instance = new Instance();
instance.Name = "XYZ";
instance.MyProperty = 11;
List<Instance> Instances = new List<Instance>();
Instances.Add(instance);
Code snippet:
public class DataSettings
{
public DataSettings()
{
Instances = new List<Instance>();
}
[DefaultValue()] //How can I mention the above default value here ?
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public List<Instance> Instances { get; set; }
}
public class Instance
{
public string Name { get; set; }
public int MyProperty { get; set; }
}
As you've seen, attributes only support constant values, so you cannot set a complex value in an attribute. If you want to set a default value for a complex property during deserialization, a good approach is to use a serialization callback method, as shown below.
The idea is to add a method to your class which the serializer will call after deserialization is complete for the object. The callback must be a void method that accepts a StreamingContext as its only parameter, and it must be marked with an [OnDeserialized] attribute. The name of the method does not matter.
Inside the callback method you can check whether the Instances list was populated, and if not, you can set the default value as you require.
public class DataSettings
{
public DataSettings()
{
Instances = new List<Instance>();
}
public List<Instance> Instances { get; set; }
[OnDeserialized]
internal void SetDefaultValuesAfterDeserialization(StreamingContext context)
{
if (Instances == null || !Instances.Any())
{
Instances = new List<Instance>
{
new Instance { Name = "XYZ", MyProperty = 11 }
};
}
}
}
Here is a working fiddle to demonstrate the concept: https://dotnetfiddle.net/uCGP5X

Is there a way to pass a class property as a parameter to a method?

I am trying to find a way to take a class's property and pass it to a method along with another variable to update the property based on conditions. For example
The class
public class MyClass{
public string? Prop1 { get; set; }
public string? Prop2 { get; set; }
public bool? Prop3 { get; set; }
public DateTime? Prop4 { get; set; }
... etc...
}
Test code I would like to get to work...:
var obj = new MyClass();
MyCheckMethod(ref obj.Prop1, someCollection[0,1]);
in the method:
private void MyCheckMethod(ref Object obj, string value)
{
if (!string.isnullorempty(value))
{
// data conversion may be needed here depending on data type of the property
obj = value;
}
}
I want to be able to pass any property of any class and update the property only after validating the value passed in the method. I was hoping I could do this with generics, but I haven't yet found a way to do so. Or if I am over complicating what I need to do.
The problem is that there may be a bit more to the validation of the passed in value than just a simple isnullorempy check.
I also thought about doing something like this:
private void MyCheckMethod(ref object obj, Action action)
Then I could do something like this:
...
MyCheckMethod(ref obj.Prop1, (somecollection[0,1]) => {
... etc....
})
So I am looking for some guidance on how to proceed.
updated info:
The incoming data is all in string format (this is how a 3rd party vendor supplies the data). The data is supplied via API call for the 3rd party product... part of their SDK. However in my class I need to have proper data types. Convert string values to datetime for dates, string values to int for int data types, etc... . The other caveat is that if there isnt a valid value for the data type then the default value of the property should be NULL.
Additional Information:
The incoming data is always in string format.
eg:
I have to update a boolean property.
The incoming value is "". I test to see if the string Value isNullOrEmpty. It is so I dont do anything to property.
The next property datatype is decimal.
The incoming value is "0.343".
I Test to see if the string value is NullorEmpty. It isnt so I can update the property once I do a convert etc.....
Hope this helps.
Thanks
Full solution after edits:
public static class Extensions
{
//create other overloads
public static void MyCheckMethodDate<TObj>(this TObj obj,Expression<Func<TObj,DateTime>> property, string value)
{
obj.MyCheckMethod(property, value, DateTime.Parse);
}
public static void MyCheckMethod<TObj,TProp>(this TObj obj,Expression<Func<TObj,TProp>> property, string value,Func<string, TProp> converter)
{
if(string.IsNullOrEmpty(value))
return;
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if(null != propertyInfo && propertyInfo.CanWrite)
{
propertyInfo.SetValue(obj, converter(value));
}
}
}
public class Obj
{
public object Prop1{get;set;}
public string Prop2{get;set;}
public DateTime Prop3{get;set;}
}
public class Program
{
public static void Main()
{
var obj = new Obj();
obj.MyCheckMethodDate(x=>x.Prop3, "2018-1-1");
Console.WriteLine(obj.Prop3);
}
}
You can pass a lambda expression:
void DoSomething<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}else{
var name = ((MemberExpression)property.Body).Member.Name;
var value = property.Compile();
//Do whatever you need to do
}
}
To use:
DoSomething(() => obj.Property1);
You can't pass a reference to an arbitrary property. A property is basically implemented as two methods, set_Property and get_Property, and there's no way to bundle these together.
One option is to have your checker function take delegates to access the property. For example:
private void MyCheckMethod(Func<string> getter, Action<string> setter)
{
var value = getter();
var newValue = value.ToUpper();
setter(value);
}
So now you would say something like this:
public class MyClass
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
var c = new MyClass();
MyCheckMethod(() => c.Prop1, value => c.Prop1 = value);
Use reflection with compiled expressions.
This performs better than reflection and a little bit slower than native code.
It's not type safe, but you can add runtime validation.

How to create custom attribute with Func<> parameter

I have a class that helps me read data from an MS SQL database into a list of objects. For the most part it's pretty straightforward; I can assume the property name of the class matches the column name of the table and just assign it accordingly, but sometimes I need to be able to transform data.
I have created a custom attribute to put on my class properties:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class TransformDataAttribute : Attribute
{
public Func<object, string, object> TransformThisData { get; set; }
}
Now, let's say I want to create the Func on the fly, like this:
[TransformData(TransformThisData = new Func<object, string, object>((v, p) => "My name is " + v.ToString()))]
public string Name { get; set; }
The error that I am seeing is 'TransformThisData' is not a valid named attribute argument because it is not a valid attribute parameter type.
What is the best way to accomplish Func as a property attribute?
Well, here's the best I have been able to come up with.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class TransformDataAttribute : Attribute
{
public string TransformDataClass { get; set; }
// This method must contain these parameters: (object value, PropertyInfo pi)
public string TransformDataMethod { get; set; }
}
I put it on the class property, like so...
public class Tracker
{
[TransformData(TransformDataClass = "CompanyTracker.DataTransformation", TransformDataMethod = "FunkyData")]
public string FunkyData { get; set; }
}
I can have a single data transformation class with different methods of transformation:
public class DataTransformation
{
public object FunkyData(object value, PropertyInfo pi)
{
// if data is this, return that, blah, blah
return value;
}
}
A static utility method for interpreting the three parameters:
public static object CallThisMethod(string className, string methodName, object[] parms)
{
Type type = Type.GetType(className);
MethodInfo theMethod = type.GetMethod(methodName);
object classInstance = Activator.CreateInstance(type);
return theMethod.Invoke(classInstance, parms);
}
...and then in my ADO Helper code, when it comes to assigning values to properties:
TransformDataAttribute attr = Utility.GetPropertyAttribute<TransformDataAttribute>(pi);
if (attr != null)
{
object[] parms = new object[] { value, pi };
value = Utility.CallThisMethod(attr.TransformDataClass, attr.TransformDataMethod, parms);
}
pi.SetValue(t, value, null);
It works. I hate to depend on Reflection of embedded strings for classes and methods, and it just doesn't seem like good design, but sometimes you just have to get things done. If anyone has a more elegant way to do this I'd be glad to hear about it.

Get value and name of property of an object

I'm trying to build some objects based on properties coming from another object. The class of the objects I need to build is
public class Data
{
public string Attribute { get; set; }
public string Value{ get; set; }
}
And the attribute will be the name of the property (and the value its value)
So I was trying to use Expressions trees to make a method that I can use for avoiding hard coding that attribute
Up to the moment I came to these couple of methods, based on a couple of posts I was reading on the net
public static string GetName<T>(Expression<Func<T>> e)
{
var member = (MemberExpression)e.Body;
return member.Member.Name;
}
public static Data BuildData<T>(Expression<Func<T>> e, appDetailCategory category)
{
var member = (MemberExpression)e.Body;
Expression strExpr = member.Expression;
var name = member.Member.Name;
var value = Expression.Lambda<Func<string>>(strExpr).Compile()();
return new Data
{
Attribute = name,
Value = value
};
}
But the line I'm trying to set the value raises an exception:
Expression of type 'AutomapperTest.Program+DecisionRequest' cannot be used for return type 'System.String'
I'm pretty sure this message it's supposed to make the error obvious but it's not for me
UPDATE:
I'm calling it this way
private static Data[] GetApplicatonDetailsFromRequest(DecisionRequest request)
{
BuildData(() => request.PubID)
//...
}
Must be member, not member.Expression.
public static Data BuildData<T>(Expression<Func<T>> e, appDetailCategory category)
{
var member = (MemberExpression)e.Body;
var name = member.Member.Name;
var value = Expression.Lambda<Func<string>>(member).Compile()();
return new Data
{
Attribute = name,
Value = value
};
}
It looks like your problem is that the type of PubID isn't a string. You've got two options, either change Data to store the value as an object or call ToString on the value returned from the property and store it. For example:
public static Data BuildData<T>(Expression<Func<T>> e)
{
var member = (MemberExpression)e.Body;
var name = member.Member.Name;
Func<T> getPropertyValue=e.Compile();
object value = getPropertyValue();
return new Data
{
Attribute = name,
Value = value.ToString()
};
}
Note that to get the value you can just compile the Func expression.

Using reflection to set properties from a dictionary

I have an object with some properties and a dictionary which holds a temporary value for each of property. The key of this dictionary is a string with the same name of the property, while the value is an object.
What I want to do is to build a save method that reads the dictionary's keys and set the corresponding property to the value found in the dictionary.
So I thought about reflection but it's not as easy as I thought.
Here's a sample class:
public class Class{
public string Property1 { get; set; }
public int Property2 { get; set; }
public Dictionary<string, object> Settings { get; set; }
public void Save()
{
foreach (string key in Settings.Keys)
{
// PSEUDOCODE
get the property called like the key
get its type
get the value of hte key in the dictionary
cast this object to the property's value
set the property to the casted object
}
}
}
The reason why I'm not posting my code is beacuse I don't understand how to do casting and similar stuff, so I wrote a little bit of pseudocode to let you understand what I'm trying to achieve.
Is there anyone that can point me to the right direction?
Here:
//Get the type
var type= this.GetType();
foreach (string key in Settings.Keys)
{
//Get the property
var property = type.GetProperty(key);
//Convert the value to the property type
var convertedValue = Convert.ChangeType(Settings[key], property.PropertyType);
property.SetValue(this, convertedValue);
}
Not tested, but it should work.

Categories

Resources