I have this scenario:
private bool form_1_Enabled = true;
private new Dictionary<string,bool> dict = new Dictionary<string,bool>()
{
{ 'is_form_1_enabled', this.form_1_Enabled }
};
for(var i in dict)
{
if (i.Value == true)
{
i.Value = false; // this should change form_1_Enabled
}
}
so, the idea is to change the passed property.
Is something like that possible?
The only solution i've found was:
(dynamic)this.GetType().GetProperty(i.Value).GetValue(this, null) = false;
As soon as you have to copy and maintain duplicate state you should think a different solution. Keeping state in sync is expensive and error prone.
Some alternatives (in no particular order)
Use the dictionary and have other code access that directly or indirectly (by indirect I mean you could have a helper function that returns a value based on some parameter).
Seems your code uses the dictionary only to loop through the private variables and set their value. Instead of a dictionary use reflection on the instance to find all private fields instances of type boolean, with additional checks as necessary like on name or an attribute marker, and (re)set the value that way.
Example:
using System.Linq;
using System.Reflection;
public void Reset()
{
foreach (var field in this.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(x=>x.Name.EndsWith("Enabled", StringComparison.OrdinalIgnoreCase) && x.FieldType == typeof(bool)))
{
field.SetValue(this, false);
}
}
Because in c# a bool is a value type it is always copied by value. If you want to copy it by reference you could write a wrapper for the value type
class A
{
private BoolWrapper form_1_Enabled = new BoolWrapper(true);
private new Dictionary<string, BoolWrapper> dict;
public A()
{
dict = new Dictionary<string, BoolWrapper>() { { "is_form_1_enabled", form_1_Enabled }, };
foreach (var i in dict)
{
if (i.Value.Value == true)
{
i.Value.Value = false;
}
}
}
public class BoolWrapper
{
public bool Value { get; set; }
public BoolWrapper(bool value) { this.Value = value; }
}
}
Related
I'm experimenting with reflection in c# for the first time (for use in a dev tool I'm building) and I was wondering if someone could help me to access the thing that I'm trying to access?
The code below is the class I'm accessing:
[Serializable]
public class MaterialVariantTarget : BaseNode, IMaterialTarget
{
public MeshRenderer m_Target;
public void ApplyValue(Material value) => m_Target.material = value;
}
The question I want to answer is what is the name of the value that ApplyValue() operate on. So in this example it would be MeshRenderer.material
I've broken the problem down into two parts. Accessing m_Target's Type (MeshRenderer) and the .material property.
The first, I've managed to access:
private Type GetTargetComponentType(Type targetNodeType)
{
foreach (var fieldInfo in targetNodeType.GetFields())
{
if (fieldInfo.Name == "m_Target")
{
var type = fieldInfo.FieldType;
return type;
}
}
return null;
}
I'm finding accessing the scond part more tricky. Is what I'm trying to do even possible and if so, how can I do it?
Many Thanks
[UPDATE]
So the consensus seems to be that I can't access the contents of the method.
I'm going to have to just resort to writing out the info I need as a string hich can then be read but its not ideal :(
Might I have more options if I were to arrange it as a property get/setter? like this:
[Serializable]
public class MaterialVariantTarget : BaseNode, IMaterialTarget
{
public MeshRenderer m_Target;
private Material m_valueProperty
{
get => m_Target.material;
set => m_Target.material = value;
}
public void ApplyValue(Material value) => m_valueProperty = value;
}
Here's two handy extensions I made to retrieve the value of a field or property of an object based on a name or a type:
public static class Extensions
{
public static object GetPropertyOrFieldByName(this object obj, string nameToSearch)
{
foreach (var field in obj.GetType().GetFields())
{
if (field.Name == nameToSearch)
{
return field.GetValue(obj);
}
}
foreach (var property in obj.GetType().GetProperties())
{
if (property.Name == nameToSearch)
{
return property.GetValue(obj);
}
}
return null;
}
public static T GetPropertyOrFieldByType<T>(this object obj) where T : Object
{
foreach (var field in obj.GetType().GetFields())
{
if (field.FieldType == typeof(T))
{
return (T)field.GetValue(obj);
}
}
foreach (var property in obj.GetType().GetProperties())
{
if (property.PropertyType == typeof(T))
{
return (T)property.GetValue(obj);
}
}
return null;
}
}
The usage you require could be implemented this way:
object target = yourMaterialVariantTarget.GetPropertyOrFieldByName("m_Target");
Material material = target.GetPropertyOrFieldByType<Material>();
material.color = Color.red;
I'm posting this question to find a simpler way of achieving a result.
We have a big IF statement that checks for NULL or string.empty. Something like this:
if (string.IsNullOrEmpty(Empl.Name) || string.IsNullOrEmpty(Empl.last) ||
string.IsNullOrEmpty(Empl.init) || string.IsNullOrEmpty(Empl.cat1) ||
string.IsNullOrEmpty(Empl.history) || string.IsNullOrEmpty(Empl.cat2) ||
string.IsNullOrEmpty(Empl.year) || string.IsNullOrEmpty(Empl.month) ||
string.IsNullOrEmpty(Empl.retire) || string.IsNullOrEmpty(Empl.spouse) ||
string.IsNullOrEmpty(Empl.children) || string.IsNullOrEmpty(Empl.bday) ||
string.IsNullOrEmpty(Empl.hire)|| string.IsNullOrEmpty(Empl.death) ||
string.IsNullOrEmpty(Empl.JobName) || string.IsNullOrEmpty(Empl.More) ||
string.IsNullOrEmpty(Empl.AndMore))
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
Any solution I've found so far to address this is time-consuming, and would require writing more code.
Is there any way to know which value is string.IsNullOrEmpty without having to change this IF statement too much? Worse-case, I can check every single statement separately, but I would prefer not doing this.
Thanks.
No, there's no "magic" function that will tell you which of a series of expression in an OR statement are true. Also, since you're using the short-circuiting version, the statement will return true after the first true condition, so the remaining expressions are not even evaluated.
However, you could do something like this:
bool[] checks = {
string.IsNullOrEmpty(Empl.Name) , string.IsNullOrEmpty(Empl.last) ,
string.IsNullOrEmpty(Empl.init) , string.IsNullOrEmpty(Empl.cat1) ,
string.IsNullOrEmpty(Empl.history) , string.IsNullOrEmpty(Empl.cat2) ,
string.IsNullOrEmpty(Empl.year) , string.IsNullOrEmpty(Empl.month) ,
string.IsNullOrEmpty(Empl.retire) , string.IsNullOrEmpty(Empl.spouse) ,
string.IsNullOrEmpty(Empl.children) , string.IsNullOrEmpty(Empl.bday) ,
string.IsNullOrEmpty(Empl.hire) , string.IsNullOrEmpty(Empl.death) ,
string.IsNullOrEmpty(Empl.JobName) , string.IsNullOrEmpty(Empl.More) ,
string.IsNullOrEmpty(Empl.AndMore)
};
if(checks.Any())
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
now the checks variable holds the result of each expression.
I find this sort of an more elegant way to use ModelState.isValid.
Some reference: What is ModelState.IsValid valid for in ASP.NET MVC in NerdDinner?
For your model, you can add following annotation:
[Required(AllowEmptyStrings= false)]
public string Boo { get; set; }
When you do validation, try:
if (!ModelState.IsValid)
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
Yes, write your own string extension method that does the same check, but also takes in a List and add the field name to the list. Declare the list of strings before the if and you will have a list of offending fields where your comment is.
This can be improved upon with a bit of reflection to automatically get the name and maybe make a few optimizations but it is on the right track.
Keep in mind that the first condition that violates the if statement will cause it to fail, so you will get an incomplete list (of one item) unless your if is constructed differently.
public static class StringExtensions
{
public static bool CheckIsNullOrEmptyAndListIt(this string field, string fieldName, List<string> naughties)
{
var result = String.IsNullOrEmpty(field);
if (result == true)
{
naughties.Add(fieldName);
}
return result;
}
}
}
using System.IO;
using System;
using System.Linq;
public class Program
{
public class Dog
{
public static string Name {get;set;}
public static string Race {get;set;}
}
public static bool validate(Dog dog)
{
bool val = true;
var y = dog.GetType()
.GetProperties()
.Select(p =>
{
object value =p.GetValue(dog,null);
if(string.IsNullOrEmpty(value.ToString())){ val=false; return false;}
else return true;
})
.ToArray();
return val;
}
public static void Main()
{
Dog dog= new Dog();
Dog.Name = "Peter";
Dog.Race = "";
if(validate(dog))
{
Console.WriteLine("Hello, World!");
}
}
}
You can use something like this :
public static class ValidationHelper
{
public static IEnumerable<string> FindEmptyProperties<T>(T target, params Expression<Func<T, string>>[] propertySelectors)
{
foreach (var propertySelector in propertySelectors)
{
if (string.IsNullOrEmpty(propertySelector.Compile()(target)))
{
var memberExpr = propertySelector.Body as MemberExpression;
yield return memberExpr.Member.Name;
}
}
}
}
Usage :
var failed = ValidationHelper.FindEmptyProperties(Empl, x => x.Name, x => x.last, x => x.init, x => x.cat1).ToList();
if (failed.Any())
{
throw new InvalidOperationException(
string.Format("Error: {0} is missing",
string.Join(", ", failed)));
}
If you use ASP.NET MVC maybe use DataAnnotations...
For the general c# context consider PostSharp aspect oriented library! Geat project!
Otherwise: Maybe a reflection solution using plain .NET ? (Created just for you! I think i keep for some own projects maybe)
Works with different types and you can control the targeted bindingflags.
Provides a common base class for your data transfer objects. (dto)
Reflection is performance optimized and working for generics as well!
public class Program
{
public void Main()
{
Empl test = new Empl()
{
TestProp = "blub",
TestInt = 1
};
if (test.ValidateProperties(Validations.CheckEmptyStringsAndZeroInts))
{
Console.WriteLine("validation passed");
}
else
{
Console.WriteLine("validation failed");
}
}
}
private static class Validations
{
//put this in a static class with standard checks
public static Func<object, bool> CheckEmptyStringsAndZeroInts = o =>
{
if (o is string && string.IsNullOrEmpty((string)o))
{
return false;
}
else if (o is int && ((int) o) == 0)
{
return false;
}
// ignore other property types
return true;
};
}
// Derive all your models like this. deriving from an Empl class is still valid and working!
//[IncludeBindingFlagsForPropertyReflctionAttribute(/*your custom binding flags*/)] //can also override the binding flags in derived classes!
public class Empl : DtoBase<Empl>
{
public string TestProp { get; set; }
public int TestInt { get; set; }
// Your properties here
}
// Helps you to control the targeted properties. you can filter for public or protected members for example
public class IncludeBindingFlagsForPropertyReflctionAttribute : Attribute
{
public BindingFlags BindingFlags { get; }
public IncludeBindingFlagsForPropertyReflctionAttribute(BindingFlags propertySearchBindingFlags)
{
BindingFlags = propertySearchBindingFlags;
}
}
//Looks much. But used once as base class can do those validations for you
[IncludeBindingFlagsForPropertyReflction(BindingFlags.Public | BindingFlags.Instance)]
public abstract class DtoBase<TDto> where TDto : DtoBase<TDto>
{
private static Dictionary<Type, List<PropertyInfo>> DtoPropertyInfosStorage { get; }
private List<PropertyInfo> DtoPropertyInfos => DtoPropertyInfosStorage[typeof (TDto)];
static DtoBase()
{
DtoPropertyInfosStorage = new Dictionary<Type, List<PropertyInfo>>();
Type tDto = typeof (TDto);
var includeBindingFlagsForProperty = GetAttribute(tDto);
BindingFlags defaultTargetFlags = BindingFlags.Instance | BindingFlags.Public;
DtoPropertyInfosStorage.Add(typeof(TDto), new List<PropertyInfo>(typeof(TDto).GetProperties(includeBindingFlagsForProperty?.BindingFlags ?? defaultTargetFlags)));
}
private static IncludeBindingFlagsForPropertyReflctionAttribute GetAttribute(Type dtoType)
{
bool stopRecursion = !dtoType.IsSubclassOf(typeof(DtoBase<TDto>));
var includeBindingFlagsForProperty = dtoType.GetCustomAttributes(typeof(IncludeBindingFlagsForPropertyReflctionAttribute)).FirstOrDefault();
if (includeBindingFlagsForProperty == null && !stopRecursion)
{
return GetAttribute(dtoType.BaseType);
}
return null;
}
/// <summary>
/// You can handle your validation type in you validation function yourself.
/// </summary>
public bool ValidateProperties(Func<object, bool> validationFunction)
{
foreach (KeyValuePair<Type, List<PropertyInfo>> dtoPropertyInfo in DtoPropertyInfosStorage)
{
foreach (PropertyInfo propertyInfo in DtoPropertyInfos)
{
if (!validationFunction(propertyInfo.))
{
return false;
}
}
}
return true;
}
/// <summary>
/// You can pass your targeted property type like string to TPropertyType
/// <![CDATA[ Example:
/// if(ValidateProperties<string>(validate => !string.IsNullOrEmpty(validate)))
/// {
/// properties not empty?
/// }
/// ]]]]>
/// </summary>
public bool ValidateProperties<TPropertyType>(Func<TPropertyType, bool> validationFunction)
{
List<PropertyInfo> targetPropertyInfos =
DtoPropertyInfos.Where(prop => prop.PropertyType == typeof (TPropertyType))
.ToList();
foreach (PropertyInfo dtoPropertyInfo in targetPropertyInfos)
{
if (validationFunction((TPropertyType) dtoPropertyInfo.GetValue(this)))
{
return false;
}
}
return true;
}
}
I've a class "TradingStrategy", with n subclasses ("Strategy1, Strategy2 etc...").
I've a simple UI from which i can choose a subclass (I've got all the subclasses of the "TradingStrategy" class pretty easily).
What i want now is to print (in a datagridview, listbox, combobox, doesn't matter) all the public parameters of the choosen subclass.
I would prefer not to instantiate the subclasses.
namespace BackTester
{
class TradingStrategy
{
public string Name;
}
class MA_Test : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 12;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test()
{
}
}
class MA_Test2 : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 24;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test2()
{
}
}
}
With this code i can insert into a combo box every subclass of "TradingStrategy"
var type = typeof(TradingStrategy);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
foreach (var t in types){
if (t.Name == "TradingStrategy") continue;
boxStrategy.Items.Add(t.Name);
}
I wanna be able to, from the combobox.Text, get all the properties name and values of the corrisponding subclass.
I think I've read (and tried) every post here and in other forum. Many use reflections.
What is the simplest way to get those prop/values?
Thanks
Why not just create an interface ITradingStrategy:
public interface ITradingStrategy
{
string Name { get; }
int len { get; }
float lots { get; }
bool trendFollow { get; }
}
And have all classes inherit from the interface then pull values from interface.
As was mentioned in the comments, you have to instantiate an instance of the class in order to set some values on it.
To get the public fields/properties and their types without instantiating the objects, you can use reflection as follows:
private static Dictionary<string, Type> GetFields(Type t)
{
var fields = new Dictionary<string, Type>();
foreach (var memberInfo in t.GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
fields.Add(propertyInfo.Name, propertyInfo.PropertyType);
}
if (fieldInfo != null)
{
fields.Add(fieldInfo.Name, fieldInfo.FieldType);
}
}
return fields;
}
If you already have the object, you can get all the public fields/values with this method.
private static Dictionary<string, object> GetValues(FileInfo o)
{
var values = new Dictionary<string, object>();
foreach (var memberInfo in o.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
values.Add(propertyInfo.Name, propertyInfo.GetValue(o, null));
}
if (fieldInfo != null)
{
values.Add(fieldInfo.Name, fieldInfo.GetValue(o));
}
}
return values;
}
The following code is a very slow way to get all the types which derive from a given type, due to the way that the CLR implements GetTypes() and the fact there could be thousands of unrelated types in your code which makes the haystack to search even bigger. The only time you should use this method is if you dynamically load assemblies at runtime containing object definitions that you need to load. Unfortunately there is no other way to get this information at runtime:
var type = typeof(TradingStrategy);
var subtypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => p != type && type.IsAssignableFrom(p));
I would recommend that you store this list of types somewhere in your code, e.g. in an array, and iterate over it when you need to know all of your strategies:
private static readonly Type[] TradingStrategies =
{
typeof(Strategy1),
typeof(Strategy2),
typeof(Strategy3),
};
After reading Erik's answer. If you will never instantiate these classes, you could store this data in a configuration file, and use something like JSON.net to read it, or if you don't want to use an external library, XmlSerializer would work as well. In this case you would store each MATest as a Dictionary (which lends itself nicely to JSON.net's JObject. Using JSON.net, you would have a configuration file that looks like:
[
{
"MA_Test": {
"len": 12,
"lots": 0.1,
"trendFollow": true
},
"MA_Test2": {
"len": 24,
"lots": 0.1,
"trendFollow": true
}
}
]
Then read it with code that looks like:
public JObject ReadConfig(string configPath)
{
using (var filestream = File.Open(configPath, FileMode.Open))
using (var streamReader = new StreamReader(filestream))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var jsonSerializer = new JsonSerializer();
return jsonSerializer.Deserialize<JObject>(jsonTextReader);
}
}
Thank you all for you answers.
The simplest way I found to get the properties from an indirected instantiated class is this:
var strategy = activator.CreateInstance(Type.GetType("BackTester."+boxStrategy.Text));
foreach (FieldInfo prop in strategy.GetType().GetFields(BindingFlags.Public
| BindingFlags.Instance))
{
listBox1.Items.Add(prop.ToString() + " " + prop.GetValue(strategy));
}
Based on the code you've provided, there is no reason for there to be separate classes for each MA_Test (X DO NOT use underscores, hyphens, or any other nonalphanumeric characters.). Instead these should be the same class with different properties (not fields).
class TradingStrategy
{
public string Name { get; set; }
}
class MATest : TradingStrategy
{
// this is not needed if it is inherited by TradingStragegy
// You should be getting a warning that you are hiding
// the field/property
// public string Name { get; set; }
// Should probably be more descriptive
// e.g. LengthInFeet...
public int Length { get; set; }
public float Lots { get; set; }
// I recommended boolean properties to be prefixed with
// Is, Can, Has, etc
public bool CanTrendFollow { get; set; }
}
// Somewhere Else...
var MATests = new List<MATest>()
{
new MATest()
{
Name = "MATest",
Length = 12,
Lots = 0.1F,
CanTrendFollow = true
},
new MATest()
{
Name = "MATest",
Length = 24,
Lots = 0.1F,
CanTrendFollow = true
},
}
Now instead of costly Reflection and Activator, just create the list classes once (manually, from config or even a database), and they can be used for whatever you need.
I am reading the "Head First Object Oriented Design and Analysis" and I am stuck on page 254.
In the java code below, I am trying to convert the "Matches" method to a c# one.
public class InstrumentSpec {
private Map properties;
public InstrumentSpec(Map properties) {
if (properties == null) {
this.properties = new HashMap();
} else {
this.properties = new HashMap(properties);
}
}
public Object getProperty(String propertyName) {
return properties.get(propertyName);
}
public Map getProperties() {
return properties;
}
public boolean matches(InstrumentSpec otherSpec) {
for (Iterator i = otherSpec.getProperties().keySet().iterator();
i.hasNext(); ) {
String propertyName = (String)i.next();
if (!properties.get(propertyName).equals(
otherSpec.getProperty(propertyName))) {
return false;
}
}
return true;
}
}
And this is the C# code that I have so far:
public class InstrumentSpec
{
private IDictionary _properties;
public InstrumentSpec(IDictionary properties)
{
this._properties = properties == null ? new Hashtable() : new Hashtable(properties);
}
public object GetProperty(string propertyName)
{
return _properties.Contains(propertyName);
}
public IDictionary Properties
{
get { return _properties; }
set { _properties = value; }
}
public virtual bool Matches(InstrumentSpec otherSpec)
{
foreach (var prop in otherSpec.Properties)
{
if (!prop.Equals(otherSpec.Properties))
{
return false;
}
}
return true;
}
}
Anyone has got any idea how to make the Matching method work so that it checks if two objects match?
The Java code iterates over the dictionary keys and compares the respective property values. You're currently iterating over the key/value pairs and compare them to the dictionary.
I guess something like
foreach (var key in otherSpec.Properties.Keys)
{
if (!Properties[key].Equals(otherSpec.Properties[key]))
{
return false;
}
}
return true;
would be a better translation.
Look at your comparison:
if (!prop.Equals(otherSpec.Properties))
When do you expect any single "property" to equal the collection of "properties" which contains it? The Java code is making comparisons with the object's internal collection of "properties":
if (!properties.get(propertyName).equals(
otherSpec.getProperty(propertyName)))
Which basically means it's looping through a collection of "properties" and for each "property" in that collection it is comparing it with a similarly named "property" in another collection. But you don't make any reference to the object's collection here:
private IDictionary _properties;
You need to compare the values from one collection to the values in the other collection. Without doing any checking if the values actually exist in the collection (which I recommend doing), it might look something like this:
foreach (var prop in otherSpec.Properties.Keys)
{
if (!otherSpec.Properties[prop].Equals(_properties[prop]))
{
return false;
}
}
return true;
You could completely copy the algorithm, if thats what you want:
public virtual bool Matches(InstrumentSpec otherSpec)
{
foreach (var prop in otherSpec.Properties.Keys)
{
if (!Object.equals(properties[prop], otherSpec[prop]))
{
return false;
}
}
return true;
}
But i would advise, to use generics, to know, which types we are talking about
Try this:
var keysEqual= Properties.Keys.SequenceEqual(otherSpec.Properties.Keys);
var valuesEqual = Properties.Values.SequenceEqual(otherSpec.Properties.Values);
if(keysEqual && valueEqual)
{
//objects have the same properties and values
}
I have seen various questions raised and answered where we can invoke a private setter using reflection such as this one:
Is it possible to get a property's private setter through reflection?
However I have some code which has a property i need to set but cant because there is no setter, I cant add a setter as this isn't my code. Is there a way to somehow set the value using reflection in this scenario?
I do not suggest doing this on your application but for testing purpose it may be usefull...
Assuming you have:
public class MyClass
{
public int MyNumber {get;}
}
You could do this if its for test purpose, I would not suggest to use this in your runtime code:
var field = typeof(MyClass).GetField("<MyNumber>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(anIstanceOfMyClass, 3);
You have to keep in mind that a property is just syntactic sugar for a pair of methods. One method (the getter) returns a value of the property type and one method (the setter) accepts a value of the property type.
There is no requirement that the getter and setter actually get or set anything. They're just methods, so they're allowed to do anything. The only requirement is that the getter return a value. From the outside there's no way you can really tell if there is a backing field. The getter could be getting computed every time it's called. It may be based on other properties.
So, no, there isn't really any way in general to "set" a property that doesn't have a setter.
Adding a practical use case to #abyte0's answer.
Some libraries make use of reflection to set properties this way. For example, see this sample code from https://github.com/natemcmaster/CommandLineUtils:
using System;
using McMaster.Extensions.CommandLineUtils;
public class Program
{
public static int Main(string[] args)
=> CommandLineApplication.Execute<Program>(args);
[Option(Description = "The subject")]
public string Subject { get; } = "world";
[Option(ShortName = "n")]
public int Count { get; } = 1;
private void OnExecute()
{
for (var i = 0; i < Count; i++)
{
Console.WriteLine($"Hello {Subject}!");
}
}
}
Behind the scenes, this syntax is implemented with this code:
public static SetPropertyDelegate GetPropertySetter(PropertyInfo prop)
{
var setter = prop.GetSetMethod(nonPublic: true);
if (setter != null)
{
return (obj, value) => setter.Invoke(obj, new object?[] { value });
}
else
{
var backingField = prop.DeclaringType.GetField($"<{prop.Name}>k__BackingField", DeclaredOnlyLookup);
if (backingField == null)
{
throw new InvalidOperationException(
$"Could not find a way to set {prop.DeclaringType.FullName}.{prop.Name}. Try adding a private setter.");
}
return (obj, value) => backingField.SetValue(obj, value);
}
}
The practical value here is having the code express that the only way a value should be set is through a command line invocation. This is allowed: hello.exe -s world but this is not: Subject = "some other value";
Gleaning from the excellent answer above by #(Dan Solovay), we can now do something like this (made it easy to paste into LinqPad):
#nullable enable
void Main()
{
var model = new MyModel();
Console.WriteLine(model.Season);
var setter = GetSetterForProperty<MyModel, SeasonEnum>(x => x.Season);
setter?.Invoke(model, SeasonEnum.Summer);
Console.WriteLine(model.Season);
}
enum SeasonEnum
{
Unknown,
Spring,
Summer,
Autumn,
Winter
}
class MyModel
{
public SeasonEnum Season { get; }
}
private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
private static Action<T, TValue>? GetSetterForProperty<T, TValue>(Expression<Func<T, TValue>> selector) where T : class
{
var expression = selector.Body;
var propertyInfo = expression.NodeType == ExpressionType.MemberAccess ? (PropertyInfo)((MemberExpression)expression).Member : null;
if (propertyInfo is null)
{
return null;
}
var setter = GetPropertySetter(propertyInfo);
return setter;
static Action<T, TValue> GetPropertySetter(PropertyInfo prop)
{
var setter = prop.GetSetMethod(nonPublic: true);
if (setter is not null)
{
return (obj, value) => setter.Invoke(obj, new object?[] { value });
}
var backingField = prop.DeclaringType?.GetField($"<{prop.Name}>k__BackingField", DeclaredOnlyLookup);
if (backingField is null)
{
throw new InvalidOperationException($"Could not find a way to set {prop.DeclaringType?.FullName}.{prop.Name}. Try adding a private setter.");
}
return (obj, value) => backingField.SetValue(obj, value);
}
}
Could you use "propertyInfo" approach. See the example below:
With a class like this:
public class MyClass {
public string MyAttribute{ get; } // --> blocked attribute
}
Use this code to change property value:
var instanceOfMyClass = new MyClass();
typeof(MyClass).GetProperty("MyAttribute")?.SetValue(instanceOfMyClass , "SomeValue");
or maybe you can write it a little more "elegant" using nameof.
typeof(MyClass).GetProperty(nameof(MyClass.MyAttribute))?.SetValue(instanceOfMyClass , "SomeValue");