Set Object Property of Object Property by Reflection - c#

I used this SO Question to retrieve a property of an object using reflection. The property I retrieved is another object that has a property called Value that I need to access. All of the potential objects that I retrieve using reflection derive from the same class EntityField and therefore all have a Value property. I saw this SO question that hinted at how I might be able to access the Value property, but I couldn't quite put together the correct code. How can I access the Value property on an object retrieved by reflection?
My Attempts
var parent = entity.GetType().GetProperty("Property");
parent.GetType().GetProperty("Value").SetValue(parent, newValue); // parent.GetType() is null
(parent as EntityField<T>).Value = newValue; // Not sure how to dynamically set T since it could be any system type
Main (Original Code)
private static void SetValues(JObject obj, EntityBase entity)
{
// entity.GetType().GetProperty("Property") returns an EntityField Object
// I need to set EntityField.Value = obj["Value"]
// Current code sets EntityField = obj["Value"] which throws an error
entity.GetType().GetProperty("Property").SetValue(entity, obj["Value"], null);
}
EntityField
public class EntityField<T> : EntityFieldBase
{
private Field _Field;
private T _Value;
public EntityField(Field field, T value){
this._Field = field;
this._Value = value;
}
public Field Field
{
get
{
return this._Field;
}
set
{
if (this._Field != value)
{
this._Field = value;
}
}
}
public T Value
{
get
{
return this._Value;
}
set
{
if (!EqualityComparer<T>.Default.Equals(this._Value, value))
{
this._Value = value;
this._IsDirty = true;
}
}
}
}

Try this:
entity.GetType().GetProperty("Value").SetValue(entity, obj["Value"], null);
You need to specify the name of the property in the GetProperty() method. I suspect there was no such property called 'Property' :)
Edit: After reading your comments try
entity.Property.GetType().GetProperty("Value").SetValue(entity, obj["Value"], null);

Tried the following in LinqPad and it worked...
class TestChild<T>
{
public T ChildProperty { get; set; }
}
class TestParent<T>
{
public TestChild<T> ParentProperty { get; set; }
}
void Main()
{
var instance = new TestParent<string>
{
ParentProperty = new TestChild<string>()
};
instance.GetType()
.GetProperty("ParentProperty")
.GetValue(instance)
.GetType()
.GetProperty("ChildProperty")
.SetValue(instance.ParentProperty, "Value");
Console.WriteLine(instance.ParentProperty.ChildProperty);
}

Related

Get and Set String Property returning Empty Property

I'm trying to get and set a property using the following code.
But the when trying to print the property using Console,it returns an empty string.Why is the property not getting set?
using System;
public class Program
{
public static void Main()
{
myclass x=new myclass();
x.myproperty="test";
Console.WriteLine(x.myproperty);
}
class myclass{
string sample;
public string myproperty
{
get { return sample;}
set {sample=myproperty;}
}
}
}
In setter you should use value to assign new value to underlying field
use this instead
public string myproperty
{
get { return sample; }
set { sample = value; }
}
or in C#7
public string myproperty
{
get => sample;
set => sample = value;
}
Edit
As #bradbury9 mentioned, you can also use auto-implemented properties, of course this is the case if you don't want any other logic in getter and setter than just getting and setting the field, if this is the case you can use below snippet
public string myproperty { get; set; }
value keyword is important for setting the value. In Visual Studio you can use propfull + double tab to avoid such common mistakes. It will create full property through shortcuts.
Here is the solution
public static void Main()
{
myclass x = new myclass();
x.myproperty = "test";
Console.WriteLine(x.myproperty);
}
class myclass
{
string sample;
public string myproperty
{
get { return sample; }
set { sample = value; }
}
}
If you just want to return null instead of empty string. This works even when you deserialize your Json:
class myclass
{
string sample;
[JsonProperty("my_property")]
public string My_property
{
get { return sample; }
set { sample = string.IsNullOrEmpty(value) ? null : value; }
}
}

Can I use constructor logic with object initializer syntax?

i.e.
MyClass myClass = new MyClass() { Value = 5 };
I have a bunch of constructor calls like the one above, but now I've realized I need to add logic to the constructor, which was a massive oversight. Currently I have no constructor, so just a blank implicit default constructor.
The below code should explain my problem.
Edit: I'm not actually doing validation, that's just a simple example of constructor logic
class Program
{
static void Main(string[] args)
{
Console.WriteLine(new Test(1) + " should be true");
Console.WriteLine(new Test(0) + " should be false");
Test test = new Test(0) { Value = 1 }; // It allows this syntax, oddly, but the value that's used is the one passed as a parameter
Console.WriteLine("I wish " + test + " was true");
// This is what I have currently, but I'd like to add logic like that which exists in the parameterized constructor
//Test test = new Test() { Value = 1 } // Would ideally function just like Test(1), otherwise I have to go and change every call
// OUTPUT
// True should be true
// False should be false
// I wish False was true
Console.ReadLine();
}
}
class Test
{
public bool? IsGood { get; }
public int Value { get; set; }
// This doesn't currently exist in my class, but I'd like to add it
public Test(int value)
{
if (value == 1)
IsGood = true;
else
IsGood = false;
}
public override string ToString()
{
return IsGood.ToString();
}
}
Don't write code like this in the first place.
I'd write your code like this:
class Test
{
public static bool IsValid(int value)
{
return whatever; // test for validity here
}
public int Value { get; private set; } // Don't let anyone change it.
public Test(int value) {
if (!IsValid(value)) throw new InvalidArgumentException("value");
this.Value = value;
}
}
There, now Value is always valid; the user can know ahead of time whether it is valid or not; an attempt to set an invalid value produces an exception. This assumes that Value cannot change.
If Value can change then write it like this:
class Test
{
public static bool IsValid(int value)
{
return whatever; // test for validity here
}
private int value;
public int Value { get { return value; }
set
{
if (!IsValid(value)) throw new InvalidArgumentException("value");
this.value = value;
}
}
public Test(int value) {
this.Value = value;
}
}
Now the value is again always legal.
If it is legal for value to be invalid, then:
class Test
{
public bool IsValid
{
get
{
return whatever; // test for validity here
} // read-only property
}
public int Value { get; set; }
public Test(int value) {
this.Value = value;
}
}
Now the value can be any integer and whether it is valid or not can be tested dynamically.
Can I set members outside of a constructor while still using logic in the constructor?
Meaning, what, exactly?
Using the object initializer syntax, a constructor still runs. You may even choose which one to use, through the normal constructor overload syntax (which you seem to show, but you say it's not in your class?). The code in your constructor looks at the parameter value that is passed to it, not the property Value (which it doesn't even set). But if you meant for the two to work together, then sure…you can set the property in the constructor and set IsGood in the Value property setter.
If you're going to do it that way, then I would not bother with the logic in the constructor at all. Just set the Value property and let its setter do the rest of the work:
class Test
{
public bool? IsGood { get; private set; }
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
IsGood = _value == 1;
}
}
public Test(int value)
{
Value = value;
}
}
I should point out that the semantics of the above is slightly different from what you seem to have started with. That is, the Value property is not read-only, and so can be set at any time. So, similarly, the IsGood property can change at any time. You previously had declared it as read-only and it was settable only in the constructor.
It's not clear from your question whether that's a problem or not. If you want IsGood to be strictly read-only (i.e. without even a private setter), then it won't be possible to do literally what you're asking for, because in the object initializer syntax, it relies on setting member properties after the constructor has already returned.
For the moment, I'll assume it's not a problem to add the private setter to the IsGood property.
Note that since IsGood apparently depends solely on the value of Value, you could even implement the above like this:
class Test
{
public bool? IsGood => _value != null ? _value == 1 : (bool?)null;
private int? _value;
public int Value
{
get { return _value ?? 0; }
set { _value = value; }
}
public Test(int value)
{
Value = value;
}
}
That is, don't even bother storing a value for IsGood. Just return the appropriate value based on the current state of the Value property (null if it's never been set, true if it's currently set to 1, and false otherwise).

Dynamically identifying properties in C#

Is there a way to dynamically identify design time properties in C#? For example:
class MyClass
{
public string MyProperty1 { get; set; }
}
And then reference it something like this:
string myVar = "MyProperty1";
MyClass.myVar = "test";
If you want to set the value of a property at runtime and the name of the property is only known at runtime you need to use Reflection. Here's an example:
public class MyClass
{
public string MyProperty1 { get; set; }
}
class Program
{
static void Main()
{
// You need an instance of a class
// before being able to set property values
var myClass = new MyClass();
string propertyName = "MyProperty1";
// obtain the corresponding property info given a property name
var propertyInfo = myClass.GetType().GetProperty(propertyName);
// Before trying to set the value ensure that a property with the
// given name exists by checking for null
if (propertyInfo != null)
{
propertyInfo.SetValue(myClass, "test", null);
// At this point you've set the value of the MyProperty1 to test
// on the myClass instance
Console.WriteLine(myClass.MyProperty1);
}
}
}
how about simply implementing an indexer on your class
public class MyClass
{
public string MyProperty1 { get; set; }
public object this[string propName]
{
get
{
return GetType().GetProperty(propName).GetValue(this, null);
}
set
{
GetType().GetProperty(propName).SetValue(this, value, null);
}
}
}
and then you can do something very similar
var myClass = new MyClass();
string myVar = "MyProperty1";
myClass[myVar] = "test";
Yes, of course you can. You need to get a FieldInfo object relating to the property that you want to set.
var field = typeof(MyClass).GetField("MyProperty1");
then from that field info object, you can set the value of any instance of that class.
field.SetValue(myinstanceofmyclass, "test");
See MSDN: FieldInfo for other fun stuff you can do with reflection.

What's the order of execution in property setters when using IDataErrorInfo?

Situation: Many times with WPF, we use INotifyPropertyChanged and IDataErrorInfo to enable binding and validation on our data objects. I've got a lot of properties that look like this:
public SomeObject SomeData
{
get { return _SomeData; }
set { _SomeData = value; OnPropertyChanged("SomeData"); }
}
Of course, I have an appropriate overridden IDataErrorInfo.this[] in my class to do validation.
Question: In a binding situation, when does the validation code get executed? When is the property set? When is the setter code executed? What if the validation fails?
For example:
User enters new data.
Binding writes data to property.
Property set method is executed.
Binding checks this[] for validation.
If the data is invalid, the binding sets the property back to the old value.
Property set method is executed again.
This is important if you are adding "hooks" into the set method, like:
public string PathToFile
{
get { return _PathToFile; }
set
{
if (_PathToFile != value && // prevent unnecessary actions
OnPathToFileChanging(value)) // allow subclasses to do something or stop the setter
{
_PathToFile = value;
OnPathToFileChanged(); // allow subclasses to do something afterwards
OnPropertyChanged("PathToFile");
}
}
}
If you want fine-grained control over the timing of validation, you can have it:
private Dictionary<string, string> Errors = new Dictionary<string, string>();
private object _MyProperty;
public object MyProperty
{
get { return _MyProperty; }
set
{
Errors["MyProperty"] = null;
if (value == _MyProperty)
{
return;
}
ValidateMyProperty(value); // may set Errors["MyProperty"]
if (Errors["MyProperty"] == null)
{
_MyProperty = value;
OnPropertyChanged("MyProperty");
}
}
}
public string this[string propertyName]
{
return Errors[propertyName];
}
No matter when data error information is requested and who's requesting it, it always returns the property's validation status as of the last time something tried to set the property.
Note that if you work at it, you can encapsulate the logic thusly:
public object MyProperty
{
set { _MyProperty = Validate("MyProperty", value, _MyProperty); }
}
private Dictionary<string, Func<object, string>> ValidationFunctions;
private object Validate(string propertyName, object value, object field)
{
Errors[propertyName] = null;
if (value == field)
{
return;
}
if (!ValidationFunctions.ContainsKey(propertyName))
{
return value;
}
Errors[propertyName] = ValidationFunctions[propertyName](value);
return (Errors[propertyName] == null)
? value
: field;
}
}

.NET Reflection set private property

If you have a property defined like this:
private DateTime modifiedOn;
public DateTime ModifiedOn
{
get { return modifiedOn; }
}
How do you set it to a certain value with Reflection?
I've tried both:
dto.GetType().GetProperty("ModifiedOn").SetValue(dto, modifiedOn, null);
and
dto.GetType().GetProperty("modifiedOn").SetValue(dto, modifiedOn, null);
but without any success. Sorry if this is a stupid question but it's the first time I'm using Reflection with C#.NET.
That has no setter; you'd need:
public DateTime ModifiedOn
{
get { return modifiedOn; }
private set {modifiedOn = value;}
}
(you might have to use BindingFlags - I'll try in a moment)
Without a setter, you'd have to rely on patterns / field names (which is brittle), or parse the IL (very hard).
The following works fine:
using System;
class Test {
private DateTime modifiedOn;
public DateTime ModifiedOn {
get { return modifiedOn; }
private set { modifiedOn = value; }
}
}
static class Program {
static void Main() {
Test p = new Test();
typeof(Test).GetProperty("ModifiedOn").SetValue(
p, DateTime.Today, null);
Console.WriteLine(p.ModifiedOn);
}
}
It also works with an auto-implemented property:
public DateTime ModifiedOn { get; private set; }
(where relying on the field-name would break horribly)
You could try to set the backing field and not the property; you should use GetField() not GetProperty().
If your property doesn't have a setter, you can't call SetValue on it.
You need to set the field because you have no set property to set the property.
Additional the BindingFlags.NonPublic is needed for not public objects.
dto.GetType().
GetField("modifiedOn",
BindingFlags.NonPublic|BindingFlags.SetField|BindingFlags.Instance).
SetValue(dto, valueToSet);
If you have a private property with a setter then you can use this Extension method to set a value:
using System.Reflection;
public static class ObjectExtensions
{
public static void SetPrivateValue<T>(this T obj, string propertyName, object value)
{
var type = typeof(T);
type.GetTypeInfo().GetDeclaredProperty(propertyName).SetValue(obj, value, null);
}
}
One way to do it, and this is might be the most correct way, considering that set may or may not exist, is to use a specific accessor
var myc = new MyClass();
var pi = typeof(MyClass).GetProperty("Prop1", BindingFlags.NonPublic | BindingFlags.Instance);
if (pi.SetMethod != null) // check if you have 'set' accessor
pi.SetMethod.Invoke(myc, new object[]{ someValue });
else
{
// do nothing OR
throw new Exception("Attempted to set read-only property " + pi.Name);
}

Categories