I am not sure whether is it possible to change attribute's parameter during runtime? For example, inside an assembly I have the following class
public class UserInfo
{
[Category("change me!")]
public int Age
{
get;
set;
}
[Category("change me!")]
public string Name
{
get;
set;
}
}
This is a class that is provided by a third party vendor and I can't change the code. But now I found that the above descriptions are not accurate, and I want to change the "change me" category name to something else when i bind an instance of the above class to a property grid.
May I know how to do this?
Well you learn something new every day, apparently I lied:
What isn’t generally realised is that
you can change attribute instance values fairly
easily at runtime. The reason is, of
course, that the instances of the
attribute classes that are created are
perfectly normal objects and can be
used without restriction. For example,
we can get the object:
ASCII[] attrs1=(ASCII[])
typeof(MyClass).GetCustomAttributes(typeof(ASCII), false);
…change the value of its public variable and show that it has changed:
attrs1[0].MyData="A New String";
MessageBox.Show(attrs1[0].MyData);
…and finally create another instance
and show that its value is unchanged:
ASCII[] attrs3=(ASCII[])
typeof(MyClass).GetCustomAttributes(typeof(ASCII), false);
MessageBox.Show(attrs3[0].MyData);
http://www.vsj.co.uk/articles/display.asp?id=713
In case anyone else walks down this avenue, the answer is you can do it, with reflection, except you can't because there's a bug in the framework. Here's how you would do it:
Dim prop As PropertyDescriptor = TypeDescriptor.GetProperties(GetType(UserInfo))("Age")
Dim att As CategoryAttribute = DirectCast(prop.Attributes(GetType(CategoryAttribute)), CategoryAttribute)
Dim cat As FieldInfo = att.GetType.GetField("categoryValue", BindingFlags.NonPublic Or BindingFlags.Instance)
cat.SetValue(att, "A better description")
All well and good, except that the category attribute is changed for all the properties, not just 'Age'.
You can subclass most of the common attributes quite easily to provide this extensibility:
using System;
using System.ComponentModel;
using System.Windows.Forms;
class MyCategoryAttribute : CategoryAttribute {
public MyCategoryAttribute(string categoryKey) : base(categoryKey) { }
protected override string GetLocalizedString(string value) {
return "Whad'ya know? " + value;
}
}
class Person {
[MyCategory("Personal"), DisplayName("Date of Birth")]
public DateTime DateOfBirth { get; set; }
}
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run(new Form { Controls = {
new PropertyGrid { Dock = DockStyle.Fill,
SelectedObject = new Person { DateOfBirth = DateTime.Today}
}}});
}
}
There are more complex options that involve writing custom PropertyDescriptors, exposed via TypeConverter, ICustomTypeDescriptor or TypeDescriptionProvider - but that is usually overkill.
Did you solve the problem?
Here are possible steps to achieve an acceptable solution.
Try to create a child class, redefine all of the properties that you need to change the [Category] attribute (mark them with new). Example:
public class UserInfo
{
[Category("Must change")]
public string Name { get; set; }
}
public class NewUserInfo : UserInfo
{
public NewUserInfo(UserInfo user)
{
// transfer all the properties from user to current object
}
[Category("Changed")]
public new string Name {
get {return base.Name; }
set { base.Name = value; }
}
public static NewUserInfo GetNewUser(UserInfo user)
{
return NewUserInfo(user);
}
}
void YourProgram()
{
UserInfo user = new UserInfo();
...
// Bind propertygrid to object
grid.DataObject = NewUserInfo.GetNewUser(user);
...
}
Later Edit: This part of the solution is not workable if you have a large number of properties that you might need to rewrite the attributes. This is where part two comes into place:
Of course, this won't help if the class is not inheritable, or if you have a lot of objects (and properties). You would need to create a full automatic proxy class that gets your class and creates a dynamic class, applies attributes and of course makes a connection between the two classes.. This is a little more complicated, but also achievable. Just use reflection and you're on the right road.
Unfortunately attributes are not meant to change at runtime. You basically have two options:
Recreate a similar type on the fly using System.Reflection.Emit as shown below.
Ask your vendor to add this functionality. If you are using Xceed.WpfToolkit.Extended you can download the source code from here and easily implement an interface like IResolveCategoryName that would resolve the attribute at runtime. I did a bit more than that, it was pretty easy to add more functionality like limits when editing a numeric value in a DoubleUpDown inside the PropertyGrid, etc.
namespace Xceed.Wpf.Toolkit.PropertyGrid
{
public interface IPropertyDescription
{
double MinimumFor(string propertyName);
double MaximumFor(string propertyName);
double IncrementFor(string propertyName);
int DisplayOrderFor(string propertyName);
string DisplayNameFor(string propertyName);
string DescriptionFor(string propertyName);
bool IsReadOnlyFor(string propertyName);
}
}
For first option: This however lack of proper property binding to reflect the result back to the actual object being edited.
private static void CreatePropertyAttribute(PropertyBuilder propertyBuilder, Type attributeType, Array parameterValues)
{
var parameterTypes = (from object t in parameterValues select t.GetType()).ToArray();
ConstructorInfo propertyAttributeInfo = typeof(RangeAttribute).GetConstructor(parameterTypes);
if (propertyAttributeInfo != null)
{
var customAttributeBuilder = new CustomAttributeBuilder(propertyAttributeInfo,
parameterValues.Cast<object>().ToArray());
propertyBuilder.SetCustomAttribute(customAttributeBuilder);
}
}
private static PropertyBuilder CreateAutomaticProperty(TypeBuilder typeBuilder, PropertyInfo propertyInfo)
{
string propertyName = propertyInfo.Name;
Type propertyType = propertyInfo.PropertyType;
// Generate a private field
FieldBuilder field = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
// Generate a public property
PropertyBuilder property = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType,
null);
// The property set and property get methods require a special set of attributes:
const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;
// Define the "get" accessor method for current private field.
MethodBuilder currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, getSetAttr, propertyType, Type.EmptyTypes);
// Intermediate Language stuff...
ILGenerator currGetIl = currGetPropMthdBldr.GetILGenerator();
currGetIl.Emit(OpCodes.Ldarg_0);
currGetIl.Emit(OpCodes.Ldfld, field);
currGetIl.Emit(OpCodes.Ret);
// Define the "set" accessor method for current private field.
MethodBuilder currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName, getSetAttr, null, new[] { propertyType });
// Again some Intermediate Language stuff...
ILGenerator currSetIl = currSetPropMthdBldr.GetILGenerator();
currSetIl.Emit(OpCodes.Ldarg_0);
currSetIl.Emit(OpCodes.Ldarg_1);
currSetIl.Emit(OpCodes.Stfld, field);
currSetIl.Emit(OpCodes.Ret);
// Last, we must map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
property.SetGetMethod(currGetPropMthdBldr);
property.SetSetMethod(currSetPropMthdBldr);
return property;
}
public static object EditingObject(object obj)
{
// Create the typeBuilder
AssemblyName assembly = new AssemblyName("EditingWrapper");
AppDomain appDomain = System.Threading.Thread.GetDomain();
AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assembly.Name);
// Create the class
TypeBuilder typeBuilder = moduleBuilder.DefineType("EditingWrapper",
TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit, typeof(System.Object));
Type objType = obj.GetType();
foreach (var propertyInfo in objType.GetProperties())
{
string propertyName = propertyInfo.Name;
Type propertyType = propertyInfo.PropertyType;
// Create an automatic property
PropertyBuilder propertyBuilder = CreateAutomaticProperty(typeBuilder, propertyInfo);
// Set Range attribute
CreatePropertyAttribute(propertyBuilder, typeof(Category), new[]{"My new category value"});
}
// Generate our type
Type generetedType = typeBuilder.CreateType();
// Now we have our type. Let's create an instance from it:
object generetedObject = Activator.CreateInstance(generetedType);
return generetedObject;
}
}
Given that the PropertyGrid's selected item is "Age":
SetCategoryLabelViaReflection(MyPropertyGrid.SelectedGridItem.Parent,
MyPropertyGrid.SelectedGridItem.Parent.Label, "New Category Label");
Where SetCategoryLabelViaReflection() is defined as follows:
private void SetCategoryLabelViaReflection(GridItem category,
string oldCategoryName,
string newCategoryName)
{
try
{
Type t = category.GetType();
FieldInfo f = t.GetField("name",
BindingFlags.NonPublic | BindingFlags.Instance);
if (f.GetValue(category).Equals(oldCategoryName))
{
f.SetValue(category, newCategoryName);
}
}
catch (Exception ex)
{
System.Diagnostics.Trace.Write("Failed Renaming Category: " + ex.ToString());
}
}
As far as programatically setting the selected item, the parent category of which you wish to change; there are a number of simple solutions. Google "Set Focus to a specific PropertyGrid property".
Here's a "cheaty" way to do it:
If you have a fixed number of constant potential values for the attribute parameter, you can define a separate property for each potential value of the parameter (and give each property that slightly different attribute), then switch which property you reference dynamically.
In VB.NET, it might look like this:
Property Time As Date
<Display(Name:="Month")>
ReadOnly Property TimeMonthly As Date
Get
Return Time
End Get
End Property
<Display(Name:="Quarter")>
ReadOnly Property TimeQuarterly As Date
Get
Return Time
End Get
End Property
<Display(Name:="Year")>
ReadOnly Property TimeYearly As Date
Get
Return Time
End Get
End Property
I really don't think so, unless there's some funky reflection that can pull it off. The property decorations are set at compile time and to my knowledge are fixed
In the mean time I've come to a partial solution, derived from the following articles:
ICustomTypeDescriptor, Part 1
ICustomTypeDescriptor, Part 2
Add (Remove) Items to (from) PropertyGrid at Runtime
Basically you would create a generic class CustomTypeDescriptorWithResources<T>, that would get the properties through reflection and load Description and Category from a file (I suppose you need to display localized text so you could use a resources file (.resx))
You can change Attribute values at runtime at Class level (not object):
var attr = TypeDescriptor.GetProperties(typeof(UserContact))["UserName"].Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
attr.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(attr, username_readonly);
Related
I am trying to create a function that can return a field from its object.
Here is what I have so far.
public class Base
{
public string thing = "Thing";
public T GetAttribute<T>(string _name)
{
return (T)typeof(T).GetProperty(_name).GetValue(this, null);
}
}
What I would ideally like is to call:
string thingy = GetAttribute<string>("thing");
but I have a feeling I got the wrong end of the stick when reading up on this because I keep getting null reference exceptions.
First thing - thing is a field, not a property.
Another thing is that you have to change parameter type to get it working:
public class Base {
public string thing = "Thing";
public T GetAttribute<T> ( string _name ) {
return (T)typeof(Base).GetField( _name ).GetValue (this, null);
}
}
BTW - you can get property/field value by referencing an instance:
var instance = new Base();
var value = instance.thing;
thing is a field not a property. You should use GetField method instead of GetProperty.
Another problem is you are looking in typeof(T). You should look for the field in typeof(Base).
The whole function should be changed to
public T GetAttribute<T>(string _name)
{
return (T)GetType().GetField(_name).GetValue(this);
}
If you want to have an extension method to get field value of a type you can use this
public static class Ex
{
public static TFieldType GetFieldValue<TFieldType, TObjectType>(this TObjectType obj, string fieldName)
{
var fieldInfo = obj.GetType().GetField(fieldName,
BindingFlags.Instance | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
return (TFieldType)fieldInfo.GetValue(obj);
}
}
Use it like
var b = new Base();
Console.WriteLine(b.GetFieldValue<string, Base>("thing"));
Using BindingFlags will help you to get field value even if it is private or static field.
I have a collection of classes at my system. So, also i have a simple config file with collection of class names.
And i want to run code like this:
(object as Class).name="some text";
Where Class is his class name string from config file. May be i should use a refletion to do this?
Thank you!
You can ignore the type and directly use dynamic (that internally uses reflection)...
object obj = ...
dynamic dyn = obj;
dyn.name = "some text";
This works only if name is public. Otherwise you can use reflection...
If name is a property:
Type type = obj.GetType();
PropertyInfo prop = type.GetProperty("name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
prop.SetValue(obj, "some text");
If it is a field:
Type type = obj.GetType();
FieldInfo field = type.GetField("name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
field.SetValue(obj, "some text");
If the class name is not available at compile time, you have to use reflection or DLR (dynamic) to set the property.
See more in the answer here Set object property using reflection
It's difficult to tell what your use case is exactly, but here is another angle on your question that may help. I'm assuming that all of the classes you can specify in your config have the same property (i.e. name). If this is the case, you could have all of the classes implement the same interface, then you don't need to worry about the class name. For example:
Your interface:
public interface IName
{
string Name { get; set; }
}
A class that uses the interface:
public class Something : IName
{
public string Name { get; set; }
public string AnotherProperty { get; set; }
}
And a method that uses any object that implements the IName interface:
public void DoSomething(IName input)
{
Console.WriteLine(input.Name);
}
So now you can call the method without having to worry about class names:
var someObject = new Something();
someObject.Name = "Bob";
DoSomething(someObject);
SO Community,
So I'm learning C# and am still trying to wrap my head around reflection. In particular trying to use it to access a property of a property on a class.
I've boiled down the basic task that I'm trying to accomplish below:
public enum SIGNAL_STATE { NOT_RETRIEVED = 0, RETRIEVING = 1, RETRIEVED = 2, FAILED = 3 }
public class MyObjectClass
{
public string MyString;
private SIGNAL_STATE _state = SIGNAL_STATE.NOT_RETRIEVED;
public SIGNAL_STATE State { get { return _state; } set { _state = value;} }
}
public class NeedToReflect
{
private MyObjectClass _myObject1 = new MyObjectClass();
private MyObjectClass _myObject2 = new MyObjectClass();
private MyObjectClass _myObject3 = new MyObjectClass();
public MyObjectClass MyObject1
{
get{return _myObject1;}
set{_myObject1 = value;}
}
public MyObjectClass MyObject2
{
get{return _myObject2;}
set{_myObject2 = value;}
}
public MyObjectClass MyObject3
{
get{return _myObject3;}
set{_myObject3 = value;}
}
public static void Main(string [] args){
NeedToReflect needToReflect = new NeedToReflect();
string fieldName;
for(int idx = 1; idx<=3; ++idx)
{
fieldName = String.Format("MyObject{0}",idx);
//TODO: set state of 'MyObject' values to SIGNAL_STATE.RETRIEVING
}
}
}
edit 1:
At Yair Nevet's suggestion I'm grabbing the FieldInfo from the applicable object like,
FieldInfo fieldInfo = needToReflect.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
But from there I get hung up on accessing and setting the right 'State' Field/Property on that member field
Solution (ie. here's what I plugged in on that TODO comment):
// Determine what binding flags will give us proper access to our member field
BindingFlags bindFlags = BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance;
// Get the field for that field
FieldInfo fieldInfo = needToReflect.GetType().GetField(fieldName, bindFlags);
// Pass in the containing object that we're reflecting on to get an instance of the member field
MyObjectClass myField = (MyObjectClass) fieldInfo.GetValue(needToReflect);
// We can now directly access and edit the value in question
myField.State = SIGNAL_STATE.RETRIEVING;
That does it. Thanks to Shlomi Borovitz for pointing me in the right direction.
Thanks!
The FieldInfo object which returned by GetField has property which called FieldInfo, which returns the type of that field.
And you can query it for that type's (the type of the field) properties/fields (and whatever you want) - and get/set them.
Remember that both GetType method (of any object), and the FieldInfo.FieldType property return Type object, that you can query in reflection.
object obj =...
var field = obj.GetType().GetField(fieldName,...);
field.FieldType.GetField(...
//etc... etc...
For each field, you can query the type and for each type, you can query the fields, and get/set them.
BTW, in C# 4.0 you can use the dynamic pseudo type (it used as a type when declaring dynamic variable, but it's not a real type), and then using that variable, while assuming which properties/fields/methods that variable would have in runtime (ie, using them like they are known in compile time although they don't).
This will not work for private members (and I can't warn you enough against calling private members in reflection), but for public members, this would make your code simple and readable, like you never used reflection (although, behind the scenes [in this case] reflection would be used).
You are trying to access a Property while the member is actually a private Field:
propertyName = String.Format("MyObject{0}",idx);
Use GetField method instead for that:
needToReflect.GetType().GetField(propertyName, BindingFlags.NonPublic |BindingFlags.GetField | BindingFlags.Instance);
Go through these steps...
1) Get the Type.
2) Have an instance of that type.
3) Get the PropertyInfo for the property.
4) Call "GetSetMethod" on the PropertyInfo object. It will return a MethodInfo object.
5) Invoke the MethodInfo object using the instance of the type and a value.
Given:
class ClassyMcClass
{
public int PropertyB { get; set; }
}
class MyClass
{
public ClassyMcClass PropertyA { get; set; }
}
The following code uses reflection on a MyClass object to set the int value of PropertyB in PropertyA to NewValue:
PropertyInfo propA = typeof(MyClass).GetProperty("PropertyA");
PropertyInfo probBofA = propA.PropertyType.GetProperty("PropertyB");
// Set property B of property A, where obj is of type MyClass
probBofA.SetValue(propA.GetValue(obj), NewValue, null);
When I have this property in an abstract class:
public IList<Component> Components { get; private set; }
Then when I call:
p.GetSetMethod(true)
with p being a PropertyInfo object pointing to my property, I get null.
However if I change the property setter to protected, I can see it via reflection. Why is this? I don't seem to recall having this problem with non-abstract classes...
I assume you are calling this on an object from a derived type of your abstract class. There isn't a property setter at all on that class. It is only located on your abstract base. This is why it works when you mark it as protected. You need use your abstract class' Type instead when getting the property setter.
It's an old thread but I run into similar issue recently and none of the above worked for me. Adding my solution as it may be useful for others.
As said before if the setter of a property is private it does not exist in the inherited class. What worked for me was to go one level lower using DeclaringType of the PropertyInfo
So the code to retrieve the property with the setter would look like this:
var propertyInfo = typeof(MyClass)
.GetProperty("Components", BindingFlags.NonPublic | BindingFlags.Instance)
.DeclaringType
.GetProperty("Components", BindingFlags.NonPublic | BindingFlags.Instance);
In this case the propertyInfo contains a value for SetMethod so you can set the value using reflection.
Some brief experimentation in the C# Interactive window suggests that for a property P declared on a class A, the following works just fine:
var p = typeof(A).GetProperty("P").GetSetMethod(true)
But as soon as you attempt the same thing with a subclass of A, GetSetMethod no longer resolves the private set accessor:
// class B : A {}
var p = typeof(B).GetProperty("P").GetSetMethod(true) // produces 'null'.
In other words, what you attempted apparently only works for private accessors when the reflected type is the same as the property's declaring type.
The trick is to use the BindingFlags enumeration to specify that you want private members to be included when you obtain the PropertyInfo object:
PropertyInfo p = obj.GetType().GetProperty("Components", BindingFlags.NonPublic | BindingFlags.Instance);
The following experimentation uncovered the issue for me. Note that the base class doesn't have to be abstract to reproduce the problem.
public class Base
{
public string Something { get; private set; }
}
public class Derived : Base { }
public class MiscTest
{
static void Main( string[] args )
{
var property1 = typeof( Derived ).GetProperty( "Something" );
var setter1 = property1.SetMethod; //null
var property2 = typeof( Base ).GetProperty( "Something" );
var setter2 = property2.SetMethod; //non-null
bool test1 = property1 == property2; //false
bool test2 = property1.DeclaringType == property2.DeclaringType; //true
var solution = property1.DeclaringType.GetProperty( property1.Name );
var setter3 = solution.SetMethod; //non-null
bool test3 = solution == property1; //false
bool test4 = solution == property2; //true
bool test5 = setter3 == setter2; //true
}
}
What I learned from this and found surprising, is that the PropertyInfo on the derived type is a different instance than the PropertyInfo on the base type. Weird!
To build on the work of #piotrwolkowski
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var propertyInfo = typeof(MyClass).GetProperty("Components", flags);
// only go to the declaring type if you need to
if (!propertyInfo.CanWrite)
propertyInfo = propertyInfo.DeclaringType.GetProperty("Components", flags);
I added both public and non-public to the binding flags for my use case (it may be overkill and I don't have the time to pursue it further)
I was setting the instance of an object that inherited from an abstract base with public get and private set
Again, all credit to #piotrwolkowski
in my application that uses reflection i have two classes
public class FirstClass
{
public string someVar;
public SecondClass second;
public FirstClass()
{
second = new SecondClass();
}
}
public class SecondClass
{
public string anotherVar;
}
in my main program i have an instance of FirstClass
MainProgram()
{
Object obj = InstanceOfFirstClass() // reflected instance of first class
}
How do i set the value of anotherVar inside obj?
With public fields, this is relatively simple:
object obj = InstanceOfFirstClass();
object second = obj.GetType().GetField("second").GetValue(obj);
second.GetType().GetField("anotherVar").SetValue(second, "newValue");
If the fields were not public, then you would need to use an overload of GetField that takes a BindingFlags argument with the NonPublic flag set.
In .Net 4, you could just use dynamic:
dynamic obj = InstanceOfFirstClass();
obj.second.anotherVar = "newValue";
You can find the example which reads the field and set a value of the field via reflection in
http://msdn.microsoft.com/en-us/library/6z33zd7h.aspx.
In your case it will look like
Object myObject = InstanceOfFirstClass() // reflected instance of first class
Type myType = typeof(FirstClass);
FieldInfo myFieldInfo = myType.GetField("second",
BindingFlags.Public | BindingFlags.Instance);
// Change the field value using the SetValue method.
myFieldInfo.SetValue(myObject , //myobject is the reflected instance
value);//value is the object which u want to assign to the field);