How to set value of property where there is no setter - c#

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");

Related

Change passed argument

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; }
}
}

Pass Property and get class

This might be a trivial question but I am drawing a blank here and can't seem to find the answer online.
Basically, I am trying to create a method that takes 2 properties that are part of an INotifyPropertyChanged class as parameters (the actual properties to be used in reflection, not the property values), and keep them "in sync" like a binding.
Example
I have a class called Student with a property called int SemesterScore. I have another class called Semester with a property called int Score. Both of the classes implement IPropertyNotifyChanged.
Now, let's just assume for a moment that we can't extend any of the classes (as in my real-life scenario) and I may have multiple times in different classes I want to use this.
Basically, I want to be able to call a method in one of my classes that "links" the two properties together.. aka if one of them changes it will auto-update the other.
In non-working code, this is the basic concept:
public class Student : INotifyPropertyChanged
{
private int _semesterScore;
public int SemeseterScore
{
get { return _semesterScore; }
set { [ set property stuff with property changed] }
}
}
public class Semester: INotifyPropertyChanged
{
private int _score;
public int Score
{
get { return _score; }
set { [ set property stuff with property changed] }
}
}
public class Entry
{
public static void Main(string[] args)
{
Student student = new Student();
Semester semester = new Semester();
AttachProperties(student.SemesterScore, semester.Score); // This obviously won't work, but this is where I pass the properties in
semester.Score = 7;
Console.WriteLine(student.SemesterScore); // Output will be 7
}
public static void AttachProperties([sometype] prop1, [sometype] prop2)
{
// Sudo code
prop1.classInstance.PropertyChanged += (pe)
{
if (pe.Property == prop1.Name)
prop2.Value = prop1.Value;
}
prop2.classInstance.PropertyChanged += (pe)
{
if (pe.Property == prop2.Name)
prop1.Value = prop2.Value;
}
}
}
Is there any way to do this? I know some workarounds (aka pass the INotifyPropertyChanged classes and the property names, then do some reflection to get that to work), but the question of passing property instances around (and doing stuff with it) has come up a few times in my coding career.
One way to do this would be to use an Observable, like #itay-podhacer suggested above.
But if you want to implement using just Reflection and INotifyPropertyChanged here is how you could do it.
First, lets get SemesterScore and Student both implement INotifyPropertyChanged:
public class Student : INotifyPropertyChanged
{
private int semesterScore;
public int SemesterScore
{
get { return semesterScore; }
set
{
semesterScore = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Semester : INotifyPropertyChanged
{
private int score;
public int Score
{
get { return score; }
set
{
score = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now lets tie the properties together, in your AttachProperties helper method. In order to do this, we will make the AttachProperties method take Expression<Func<T,object> arguments so that we avoid passing magic strings and can use Reflection to retrieve the properties names.
By the way, to run this in production you probably want to memoize that reflection code for performance.
private static void AttachProperties<T1,T2>(Expression<Func<T1, object>> property1, T1 instance1, Expression<Func<T2, object>> property2, T2 instance2)
where T1 : INotifyPropertyChanged
where T2 : INotifyPropertyChanged
{
var p1 = property1.GetPropertyInfo();
var p2 = property2.GetPropertyInfo();
//A NULL or empty PropertyName in PropertyChangeEventArgs means that all properties changed
//See: https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged(v=vs.110).aspx#Anchor_1
((INotifyPropertyChanged)instance1).PropertyChanged += (_, e) =>
{
if (e.PropertyName == p1.Name || string.IsNullOrEmpty(e.PropertyName))
{
SyncProperties(p1, p2, instance1, instance2);
}
};
((INotifyPropertyChanged)instance2).PropertyChanged += (_, e) =>
{
if (e.PropertyName == p2.Name || string.IsNullOrEmpty(e.PropertyName))
{
SyncProperties(p2, p1, instance2, instance1);
}
};
}
private static void SyncProperties(PropertyInfo sourceProperty, PropertyInfo targetProperty, object sourceInstance, object targetInstance)
{
var sourceValue = sourceProperty.GetValue(sourceInstance);
var targetValue = targetProperty.GetValue(targetInstance);
if (!sourceValue.Equals(targetValue))
{
targetProperty.SetValue(targetInstance, sourceValue);
}
}
And, finally, here is the Reflection code to retrieve the PropertyInfo from the arguments:
public static class ReflectionExtension
{
public static PropertyInfo GetPropertyInfo<T>(this Expression<Func<T, object>> expression)
{
var memberExpression = GetMemberExpression(expression);
return (PropertyInfo)memberExpression.Member;
}
private static MemberExpression GetMemberExpression<TModel, T>(Expression<Func<TModel, T>> expression)
{
MemberExpression memberExpression = null;
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
if (memberExpression == null)
{
throw new ArgumentException("Not a member access", "expression");
}
return memberExpression;
}
}
With all this in place, you can keep two properties in sync now:
public class PropertySyncTests
{
public void Should_sync_properties()
{
var semester = new Semester();
var student = new Student();
AttachProperties(x => x.Score, semester, x => x.SemesterScore, student);
semester.Score = 7;
student.SemesterScore.ShouldBe(7);
}
}
I know some workarounds (aka pass the INotifyPropertyChanged classes and the property names, then do some reflection to get that to work), but the question of passing property instances around (and doing stuff with it) has come up a few times in my coding career.
This is, ultimately, the way to do it. However, I think one key trick that you might not be aware of is Expression Trees. It is possible to create a function that takes an Expression<Func<T>> as an argument, and then delve into the Expression Tree to discover the INotifyPropertyChanged instance and the property that's given in the argument. Usage could look like this:
AttachProperties(() => student.SemesterScore, () => semester.Score);
The arguments to AttachProperties in the example above would be Expressions with the following structure.
<LambdaExpression> () => student.SemesterScore
Body <MemberExpression> student.SemesterScore
Member <PropertyInfo> SemesterScore
Expression <MemberExpression> student
Member <FieldInfo> [closure class.]student
Expression <ConstantExpression> [closure]
Value [closure instance]
Notice that you're creating a closure by using student inside of the lambda expression, so to get the value of student you'll need to use reflection to get the value of the [closure class].student field. Getting the SemesterScore property is just a matter of casting the expressions correctly and getting the .Body.Member property from the passed-in lambda expression.
Subscribe to the PropertyChanged event of the semester:
Student student = new Student();
Semester semester = new Semester();
semester.PropertyChanged += Semester_PropertyChanged;
Then assign the new score to the student
private void Semester_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
student.SemesterScore = semester.Score;
}
This will trigger the student's PropertyChanged event and also update his SemesterScore if the score changed.
Take a look at Reactive UI Extensions, it will give you the ability to "observe" a property for changes and then update (or do whatever you want) once such change occurs.
It will allow you to do something like this:
student
.WhenAnyValue(item => item.SemeseterScore)
.Subscribe(item =>
{
semester.Score = item.SemeseterScore
});
semester
.WhenAnyValue(item => item.Score)
.Subscribe(item =>
{
item.SemeseterScore = semester.Score
});
You might need to add an Ignore flag at your class and turn it on and off inside the Subscribe code, so you don't create an endless loop of updates between the two classes.
Ok so I combined #StriplingWarrior and #Pedro's answers together to get my final result:
public static void AttachProperties<T1, T2>(Expression<Func<T1>> property1, Expression<Func<T2>> property2)
{
var instance1 = Expression.Lambda<Func<object>>(((MemberExpression)property1.Body).Expression).Compile()();
var iNotify1 = instance1 as INotifyPropertyChanged;
var prop1 = GetPropertyInfo(property1);
var instance2 = Expression.Lambda<Func<object>>(((MemberExpression)property2.Body).Expression).Compile()();
var iNotify2 = instance2 as INotifyPropertyChanged;
var prop2 = GetPropertyInfo(property2);
AttachProperty(prop1, iNotify1, prop2, iNotify2);
AttachProperty(prop2, iNotify2, prop1, iNotify1);
}
static void AttachProperty(
PropertyInfo property1,
INotifyPropertyChanged class1Instance,
PropertyInfo property2,
INotifyPropertyChanged class2Instance)
{
class2Instance.PropertyChanged += (_, propArgs) =>
{
if (propArgs.PropertyName == property2.Name || string.IsNullOrEmpty(propArgs.PropertyName))
{
var prop = property2.GetValue(class2Instance);
property1.SetValue(class1Instance, prop);
}
};
}
static PropertyInfo GetPropertyInfo<T1>(Expression<Func<T1>> property)
{
MemberExpression expression = null;
if (property.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)property.Body;
expression = body.Operand as MemberExpression;
}
else if (property.Body.NodeType == ExpressionType.MemberAccess)
{
expression = property.Body as MemberExpression;
}
if (expression == null)
{
throw new ArgumentException("Not a member access", nameof(property));
}
return expression.Member as PropertyInfo;
}
This works correctly both in the example I gave and in my real-life project. Thanks!

Class member as a first-class object

I was wondering if there is something in c# to be able to pass a member of a class to another function that will use this member to get a value. So get a value of a field determined only which one at runtime. Something like in other languages (PHP at least I think) that you can do
a.b = "something"
but also
a["b"] = "something";
edit: actually not so good an example since a string is used, sorry
For clarity an example of what I'd like to be able to do:
class A
{
int x;
int y;
}
void somethingsomething<T>(T class, SomeMagicFieldClass f)
{
dosomethingwith(somemethodthatgivesmethevalueoffield(class, f));
}
Where then I can call the method like this:
A a = new A();
somethingsomething(a, A.x); //hypothetical notation
somethingsomething(a, A.y);
I now have something similar where I do:
somethingsomething(a, "x");
somethingsomething(a, "y");
I then go find the field using introspection API (also trying GetProperty)
MemberInfo memberInfo = item.GetType().GetField(fieldName);
This works but the disadvantage is that the fields passed as a string won't get updated when "refactoring" fieldnames in visual studio, so I was thinking maybe there exists something like this in c# that would get refactored automatically when changing field names?
Thanks a lot for reading this boring question
Your example looks a lot like a LINQ key selector, in that form it would look like:
A a = new A();
somethingsomething(a, p => p.x);
You can do some nice refactor-friendly things with LINQ Expressions. Here is a snippet of utilty code I used for such occasions. It allows you to get the Name, Type and Value of a property (it won't work with fields without modifications). There's also a setter for the value.
public static void Main(string[] args) {
var test = new { Test1 = 42, Test2 = "123", Test3 = 3.14195 };
somethingSomething(test, t => t.Test1);
somethingSomething(test, t => t.Test2);
somethingSomething(test, t => t.Test3);
}
static void somethingSomething<TObj,TProperty>(TObj obj, Expression<Func<TObj,TProperty>> expr) {
var accessor = GetMemberAccessor(expr, obj);
String name = accessor.Name;
TProperty value = accessor.Value;
String typeName = accessor.Type.Name;
Console.WriteLine("{0} = {1} ({2})", name, value, typeName);
}
The output of that would be:
Test1 = 42 (Int32)
Test2 = 123 (String)
Test3 = 3.14195 (Double)
To make this work, I used the following helper function and class:
public static MemberAccessor<TReturn> GetMemberAccessor<TObj,TReturn>(Expression<Func<TObj, TReturn>> expr, TObj tar) {
var body = expr.Body;
MemberExpression memberExpression = null;
if (body is UnaryExpression) {
var ue = (UnaryExpression)body;
memberExpression = (MemberExpression)ue.Operand;
} else if (body is MemberExpression)
memberExpression = (MemberExpression)body;
else
throw new NotImplementedException("can't get MemberExpression");
String name = memberExpression.Member.Name;
return new MemberAccessor<TReturn>(tar, name);
}
public class MemberAccessor<T> {
private readonly PropertyDescriptor propertyDesc;
private readonly Object target;
public MemberAccessor(Object target, String propertyName) {
this.target = target;
this.propertyDesc = TypeDescriptor.GetProperties(target)[propertyName];
}
public String Name {
get { return propertyDesc.Name; }
}
public Type Type {
get { return propertyDesc.PropertyType; }
}
public T Value {
get { return (T)Convert.ChangeType(propertyDesc.GetValue(target), typeof(T)); }
set { propertyDesc.SetValue(target, value); }
}
}
Mr. Plunkett is correct; a dynamic type will do the job. Luckily, the .NET 4 team included a handy object called the ExpandoObject that solves that for you.
You asked how to
pass a member of a class to another
function that will use this member to
get a value
You can usedelegates for this
class A
{
public string aField;
public string aProperty{get{return "someval";}}
public string aMemberFunction(){return "someval";}
}
void get_a_value(Func<string> func)
{
string theValue = func();
}
// use it:
A a = new A();
get_a_value( () => a.aField);
get_a_value( () => a.aProperty);
get_a_value( () => a.aMemberFunction());
What you don't get this way, of course, is a separation of parameters for the memberfunction and the object you are passing.

Use reflection to get attribute of a property via method called from the setter

Note: This is a follow-up to an answer on a previous question.
I'm decorating a property's setter with an Attribute called TestMaxStringLength that's used in method called from the setter for validation.
The property currently looks like this:
public string CompanyName
{
get
{
return this._CompanyName;
}
[TestMaxStringLength(50)]
set
{
this.ValidateProperty(value);
this._CompanyName = value;
}
}
But I would rather it look like this:
[TestMaxStringLength(50)]
public string CompanyName
{
get
{
return this._CompanyName;
}
set
{
this.ValidateProperty(value);
this._CompanyName = value;
}
}
The code for ValidateProperty that is responsible for looking up the attributes of the setter is:
private void ValidateProperty(string value)
{
var attributes =
new StackTrace()
.GetFrame(1)
.GetMethod()
.GetCustomAttributes(typeof(TestMaxStringLength), true);
//Use the attributes to check the length, throw an exception, etc.
}
How can I change the ValidateProperty code to look for attributes on the property instead of the set method?
As far as I know, there's no way to get a PropertyInfo from a MethodInfo of one of its setters. Though, of course, you could use some string hacks, like using the name for the lookup, and such. I'm thinking something like:
var method = new StackTrace().GetFrame(1).GetMethod();
var propName = method.Name.Remove(0, 4); // remove get_ / set_
var property = method.DeclaringType.GetProperty(propName);
var attribs = property.GetCustomAttributes(typeof(TestMaxStringLength), true);
Needless to say, though, that's not exactly performant.
Also, be careful with the StackTrace class - it's a performance hog, too, when used too often.
In the class that declares the method, you could search for the property that contains that setter. It's not performant, but neither is StackTrace.
void ValidateProperty(string value)
{
var setter = (new StackTrace()).GetFrame(1).GetMethod();
var property =
setter.DeclaringType
.GetProperties()
.FirstOrDefault(p => p.GetSetMethod() == setter);
Debug.Assert(property != null);
var attributes = property.GetCustomAttributes(typeof(TestMaxStringLengthAttribute), true);
//Use the attributes to check the length, throw an exception, etc.
}
You could consider, as an alternative approach, delaying validation until later, thus removing the need to inspect the stack trace.
This example provides an attribute...
public class MaxStringLengthAttribute : Attribute
{
public int MaxLength { get; set; }
public MaxStringLengthAttribute(int length) { this.MaxLength = length; }
}
... a POCO with the attribute applied to a property...
public class MyObject
{
[MaxStringLength(50)]
public string CompanyName { get; set; }
}
... and a utility class stub that validates the object.
public class PocoValidator
{
public static bool ValidateProperties<TValue>(TValue value)
{
var type = typeof(TValue);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in props)
{
var atts = prop.GetCustomAttributes(typeof(MaxStringLengthAttribute), true);
var propvalue = prop.GetValue(value, null);
// With the atts in hand, validate the propvalue ...
// Return false if validation fails.
}
return true;
}
}

How to convert variable name to string in c#.net? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding the Variable Name passed to a Function in C#
public new Dictionary<string, string> Attributes { get; set; }
public string StringAttributes = string.Empty;
public int? MaxLength { get; set; }
public int? Size { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
protected override void OnInit(EventArgs e) {
Attributes = new Dictionary<string, string>();
Attributes.Add("MaxLength", MaxLength.ToString());
Attributes.Add("Size", Size.ToString());
Attributes.Add("Width", Width.ToString());
Attributes.Add("Height", Height.ToString());
base.OnInit(e);
}
protected override void OnPreRender(EventArgs e) {
if (Attributes != null) {
StringBuilder attributes = new StringBuilder();
foreach (var item in Attributes) {
if (!string.IsNullOrWhiteSpace(item.Value)) {
attributes.Append(item.Key + "=\"" + item.Value + "\" ");
}
}
StringAttributes = attributes.ToString();
}
}
The problem here is, instead of using Attributes.Add("MaxLength", MaxLength.ToString()); and repeat the same process for other properties, could we not just make a function that is also able to add values to the dictionary, where the keys to be added are their variable names?
Say,
public void addAttribute(object variable){
Attributes = new Dictionary<string, string>();
Attributes.Add(variable.Name, variable.Value);
}...
I guess this is also possible to do with reflection, getting all the nullable properties and looping through them then adding each to the dictionary... But for as long as there are any other ways, we would not stick to reflection.
But if reflection is the only choice, then another problem now would be how to get the nullable properties of the class...
Any help would be greatly appreciated. Thank you.
I can't think of way to do it without reflection.
In order to get all the nullable properties you can you similar code to this:
GetType().GetProperties()
.Where(property =>
property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
Usage example that fills attributes dictionary:
PropertyInfo[] typeProperties = GetType().GetProperties();
var nullableProperties = typeProperties.Where(property =>
property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>));
var attributes = new Dictionary<string, string>();
foreach (var nullableProperty in nullableProperties)
{
object value = nullableProperty.GetValue(this,null);
attributes.Add(nullableProperty.Name, value == null ?
string.Empty : value.ToString());
}
I'm not sure I fully understand your question without more context, but perhaps this is helpful
If the concern is over reflection overhead for multiple invocations:
Cache that information.
try EmitMapper to fill in values
try AutoMapper to fill in the values
If the problem is getting a variable name via strongly typed compilation then you can use
The Member class I saw on a post from Oliver Hhanappi. Examples of its use are here on my blog
Below is my complete solution. I would say your best bet is to use reflection, as what you're asking is sort of a meta-task. As far as how do you know which properties to add, I would suggest defining your own attribute and applying it to the fields/properties that you want to inspect.
Usage:
Dictionary<string, string> attributes = Inspector<MyClass>.Inspect(target);
The reflection in my sample code is executed once per type inspected, as it is executed within the static constructor of my generic Inspect class:
// apply this attribute to any properties or fields that you want added to the attributes dictionary
[AttributeUsage(
AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Interface,
AllowMultiple = true, Inherited = true)]
public class InspectAttribute : Attribute
{
// optionally specify the member name explicitly, for use on classes, structs, and interfaces
public string MemberName { get; set; }
public InspectAttribute() { }
public InspectAttribute(string memberName)
{
this.MemberName = memberName;
}
}
public class Inspector<T>
{
// Inspector is a generic class, therefore there will be a separate instance of the _InspectActions variable per type
private static List<Action<Dictionary<string, string>, T>> _InspectActions;
static Inspector()
{
_InspectActions = new List<Action<Dictionary<string, string>, T>>();
foreach (MemberInfo m in GetInspectableMembers(typeof(T)))
{
switch (m.MemberType)
{
case MemberTypes.Property:
{
// declare a separate variable for variable scope with anonymous delegate
PropertyInfo member = m as PropertyInfo;
// create an action delegate to add an entry to the attributes dictionary using the property name and value
_InspectActions.Add(
delegate(Dictionary<string, string> attributes, T item)
{
object value = member.GetValue(item, null);
attributes.Add(member.Name, (value == null) ? "[null]" : value.ToString());
});
}
break;
case MemberTypes.Field:
{
// declare a separate variable for variable scope with anonymous delegate
FieldInfo member = m as FieldInfo;
// need to create a separate variable so that delegates do not share the same variable
// create an action delegate to add an entry to the attributes dictionary using the field name and value
_InspectActions.Add(
delegate(Dictionary<string, string> attributes, T item)
{
object value = member.GetValue(item);
attributes.Add(member.Name, (value == null) ? "[null]" : value.ToString());
});
}
break;
default:
// for all other member types, do nothing
break;
}
}
}
private static IEnumerable<MemberInfo> GetInspectableMembers(Type t)
{
// get all instance fields and properties
foreach (MemberInfo member in t.GetMembers(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.GetField | BindingFlags.GetProperty))
{
// check if the current member is decorated with an Inspect attribute
object[] inspectAttributes = member.GetCustomAttributes(typeof(InspectAttribute), true);
if (inspectAttributes != null && inspectAttributes.Length > 0)
{
yield return member;
}
}
// now look for any Inspect attributes defined at the type level
InspectAttribute[] typeLevelInspectAttributes = (InspectAttribute[])t.GetCustomAttributes(typeof(InspectAttribute), true);
if (typeLevelInspectAttributes != null && typeLevelInspectAttributes.Length > 0)
{
foreach (InspectAttribute attribute in typeLevelInspectAttributes)
{
// search for members matching the name provided by the Inspect attribute
MemberInfo[] members = t.GetMember(attribute.MemberName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy);
if (members != null && members.Length > 0)
{
foreach (MemberInfo member in members)
{
yield return member;
}
}
}
}
}
public static Dictionary<string, string> Inspect(T item)
{
// create a new attributes dictionary
Dictionary<string, string> attributes = new Dictionary<string, string>();
foreach (Action<Dictionary<string, string>, T> inspectAction in _InspectActions)
{
// execute each "inspect" action.
// This will execute the delegates we created earlier, causing entries to be added to the dictionary
inspectAction(attributes, item);
}
return attributes;
}
}
public class BasePage
{
public int? SomeValue { get; set; }
}
// example class with properties decorated with the Inspect attribute
[Inspect("SomeValue")] // also inspect the "SomeValue" property from the BasePage class
public class MyPage : BasePage
{
[Inspect]
public int? MaxLength { get; set; }
[Inspect]
public int? Size { get; set; }
[Inspect]
public int? Width { get; set; }
[Inspect]
public int? Height { get; set; }
public string GenerateAttributeString()
{
System.Text.StringBuilder attributes = new System.Text.StringBuilder();
foreach (KeyValuePair<string, string> item in Inspector<MyPage>.Inspect(this))
{
attributes.Append(item.Key + "=\"" + item.Value + "\" ");
}
return attributes.ToString();
}
}
You can use the following function to extract out the public Nullable properties from a class into the format your looking for. It also calls the getter method for the value.
This is using the same reflection use that #Elisha talked about. Also it does a .ToString() call to the value returned by the getter.
IDictionary<string, string> GetProps<T>(T DataObject)
{
if(null == DataObject)
return new Dictionary<string, string>();
var nullableProperties =
from property in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
from accessor in property.GetAccessors(false)
let returnType = accessor.ReturnType
where returnType.IsGenericType
&& returnType.GetGenericTypeDefinition() == typeof(Nullable<>)
&& accessor.GetParameters().Length == 0
select new { Name=property.Name, Getter=accessor};
return nullableProperties.ToDictionary(
x => x.Name,
x => x.Getter.Invoke(DataObject, null).ToString());
}

Categories