Is it possible to get a property's private setter through reflection? - c#

I wrote a custom serializer that works by setting object properties by reflection. Serializable classes are tagged with serializable attribute and all serializable properties are also tagged. For example, the following class is serializable:
[Serializable]
public class Foo
{
[SerializableProperty]
public string SomethingSerializable {get; set;}
public string SometthingNotSerializable {get; set;}
}
When the serializer is asked to deserialize SomethingSerializable, it gets the set method of the property and uses it to set it by doing something like this:
PropertyInfo propertyInfo; //the property info of the property to set
//...//
if (propertyInfo.CanWrite && propertyInfo.GetSetMethod() != null)
{
propertyInfo.GetSetMethod().Invoke(obj, new object[]{val});
}
This works fine, however, how can I make the property setter accessible only to the serializer? If the setter is private:
public string SomethingSerializable {get; private set;}
then the call to propertyInfo.GetSetMethod() returns null in the serializer. Is there any way to access the private setter or any other way to ensure that only the serializer can access the setter? The serializer is not guaranteed to be in the same assembly.

As you already figured out, one way to access a non-public setter is as follows:
PropertyInfo property = typeof(Type).GetProperty("Property");
property.DeclaringType.GetProperty("Property");
property.GetSetMethod(true).Invoke(obj, new object[] { value });
There is another way, though:
PropertyInfo property = typeof(Type).GetProperty("Property");
// if Property is defined by a base class of Type, SetValue throws
property = property.DeclaringType.GetProperty("Property");
property.SetValue(obj, value, BindingFlags.NonPublic | BindingFlags.Instance, null, null, null); // If the setter might be public, add the BindingFlags.Public flag.
Coming here from a search engine?
This question is specifically about accessing a non-public setter in a public property.
If both the property and setter are public, only the first example will work for you. To get the second example to work, you will need to add the BindingFlags.Public flag.
If the property is declared in a parent type and not visible to the type on which you're calling GetProperty, you won't be able to access it. You'll need to call GetProperty on a type to which the property is visible. (This doesn't affect private setters as long as the property itself is visible.)
If there are multiple declarations for the same property in the inheritance chain (via the new keyword), these examples will target the property that is immediately visible to the type on which GetProperty is called. For example, if class A declares Property using public int Property, and class B re-declares Property via public new int Property, typeof(B).GetProperty("Property") will return the property declared in B, while typeof(A).GetProperty("Property") will return the property declared in A.

Related

C# Property with no setter - how can it get set from constructor?

How come you can set a get-only auto-property from a constructor? The code below shows how you can set the property from the constructor but using reflection shows that there really isn't a setter behind the scenes. How does it get set from the constructor call if the setter method doesn't even exist in the IL?
void Main()
{
var obj = new GetOnlyProperty("original value");
Console.WriteLine(obj.Thing); //works, property gets set from ctor
//get the set method with reflection, is it just hidden..?
//nope, null reference exception
typeof(GetOnlyProperty)
.GetProperty("Thing", BindingFlags.Instance | BindingFlags.Public)
.GetSetMethod()
.Invoke(obj, new object[]{"can't set me to this, setter doen't exist!"});
}
public class GetOnlyProperty
{
public string Thing { get; }
public GetOnlyProperty(string thing)
{
Thing = thing;
}
}
A read-only automatically-implemented property is converted by the compiler into a read-only field and a read-only property. Assignments to the property in the constructor are compiled as assignments to the underlying field.
So your code here:
public class GetOnlyProperty
{
public string Thing { get; }
public GetOnlyProperty(string thing)
{
Thing = thing;
}
}
is compiled into IL as if you'd written:
public class GetOnlyProperty
{
private readonly string _thing;
public string Thing => _thing;
public GetOnlyProperty(string thing)
{
_thing = thing;
}
}
... except that _thing is really given an "unspeakable name" that wouldn't be a valid C# identifier.
A read-only property (get only) has a backing readonly field, which as you probably know, can only be set in the constructor.
hence when you have object Property { get; }
this translates to
private readonly object _property;
public object get_Property(){return _property;}
and the compiler knows that if you set the property in the constructor to set the field directly
Because a read-only property should be assigned at a time or another, otherwise its value would always be the default value of the type, and it would be completely useless.
This is what constructors are for (beside other obvious reasons), to assign values to read-only fields.

.NET Attributes on Property

If I have:
[SomeAttr]
public Int32 SomeProperty
{
get; set;
}
Is it possible for SomeAttr to tell what property it's tacked onto? Is it atleast possible to tell what Type the property is?
No. You can only do it the other way around. You can query the property (through reflection) what attributes it has:
Attribute.GetCustomAttributes(prop, true);
Where prop is an object derived from the MemberInfo class that describes the property SomeProperty. Next you iterate through the returned attributes to see if it includes yours.
No, you can't. Not directly.
What you can do, at the moment you collect the attribute, is set that attribute with extra information:
class SomeAttr: Attribute
{
public PropertyInfo Target {get;set;}
}
... and when you have collected the information:
Type type = ... // The type which holds the property.
PropertyInfo propertyInfo = typeo.GetProperty("SomeProperty");
Type propertyType = propertyInfo.PropertyType;
SomeAttr attr = propertyInfo.GetCustomAttributes(false).OfType<SomeAttr>().FirstOrDefault();
attr.Target = propertyInfo; // <== Set the target information.
This way, you can always retrieve its target member at another point in your code:
public void DoSomethingWithAttribute(SomeAttr attr)
{
PropertyInfo whichProperty = attr.Target;
}
(You can also use the baseclass MemberInfo, to support methods, fields, etc.)

Reflection without a Getter/Setter?

If I declare the following in my class:
private int? MyID = null;
And then attempt to access it via reflection, it won't be able to find it. What I mean by that is, the below will set gProp to null:
gType = refObj.GetType();
gProp = gType.GetProperty(PropertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
However, it will work fine if I instead declare it as:
private int? MyID { get; set; }
This isn't at all surprising to me as I already knew this to be the case. However, I wanted to confirm; is there anyway to make the first declaration work with reflection, or do I have provide a Getter/Setter in order for reflection to work?
Thanks!
It is a field, not a property, so it won't be returned by GetProperty. You need to use GetField method.
You need the GetField method (instead of GetProperty) for Fields.
Type.GetFields Method
Searches for the specified field, using the specified binding constraints.
Sample
// your instance
MyObject instance = new MyObject();
// get type information
Type myType = typeof(MyObject);
// get field information
FieldInfo fieldInfo = myType.GetField("MyID");
// set some value
fieldInfo.SetValue(instance, 123);
// get field value
var value = fieldInfo.GetValue(instance);
// value is 123
More Information
MSDN - Type.GetField Method (String, BindingFlags)
MSDN - SetField Method (String, BindingFlags, Object)
There is a difference between
private int? MyID = null;
and
private int? MyID { get; set; }
The first is a field, and the second is a property.
You should use the Type.GetField() method when trying to find a field via reflection.
You should create a property for accessing the variables in the class. So you manually create a property like
private int? MyID { get { return _myID;} }
The below code works, since it is Auto property in c#
private int? MyID { get; set; }
Refer http://msdn.microsoft.com/en-us/library/bb384054.aspx
My logic states the first is a simple variable, the second is a property, because it has the get/set, so for "GetProperty" to work, it needs to be a property. If you wanted a variable, you need the GetField

c#, silverlight, check if field passed by ref has an attribute applied

In a base class I have the following method for derived classes:
protected virtual void SetValue<T>(ref T field, string propertyName, T value)
{
//assign value to the field and do some other staff
...
}
Is there any way to check if fieldVar has an attribute applied (for example DataMemberAttribute)?
No, there is no way to do that, except that it looks like you're also told the property name.
If you can find the FieldInfo of the field, then you can find any attributes, but not through the ref-parameter alone.
Reading between the lines you have a set of private fields which back the values of public properties. On some or all of these properties you some data attributes attached that you want to discover.
PropertyInfo pi = this.GetType().GetProperty(propertyName);
object[] dataMemberAttributes = pi.GetCustomAttributes(typeof(DataMemberAttribute, true);
if (dataMemberAttributes.Length > 0)
{
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)dataMemberAttributes[0];
// Do stuff with the attribute.
}

Strange Effect with Overridden Properties and Reflection

I've come across a strange behaviour in .NET/Reflection and cannot find any solution/explanation for this:
class A
{
public virtual string TestString { get; set; }
}
class B : A
{
public override string TestString
{
get { return "x"; }
}
}
Since properties are just pairs of functions (get_PropName(), set_PropName()) overriding only the "get" part should leave the "set" part as it is in the base class. And this is just what happens if you try to instanciate class B and assign a value to TestString, it uses the implementation of class A.
But what happens if I look at the instantiated object of class B in reflection is this:
PropertyInfo propInfo = b.GetType().GetProperty("TestString");
propInfo.CanRead ---> true
propInfo.CanWrite ---> false(!)
And if I try to invoke the setter from reflection with:
propInfo.SetValue("test", b, null);
I'll even get an ArgumentException with the following message:
Property set method not found.
Is this as expected? Because I don't seem to find a combination of BindingFlags for the GetProperty() method that returns me the property with a working get/set pair from reflection.
EDIT:
I would expect that behaviour if I'd use BindingFlags.DeclaredOnly on GetProperties() but the default (BindingFlags.Default) takes inherited members into account and the setter of TestString clearly is inherited!
Here's a workaround:
typeof(B).GetProperty("TestString")
.GetAccessors() // { B.get_TestString() }
.First() // B.get_TestString()
.GetBaseDefinition() // A.get_TestString()
.DeclaringType // typeof(A)
.GetProperty("TestString") // A.TestString: CanRead and CanWrite
This approach should be reasonably robust. You will need to be more careful with this (BindingFlags) if you're looking for non-public accessor(s).
EDIT:
Note that this approach is different from "hardcoding" typeof(A).GetProperty("TestString") or typeof(B).BaseType.GetProperty("TestString") because it finds the actual, original type that declares the property in question. Since it isn't possible (not in C# at least) for a derived type to add new accessors to an overridden property, the property-declaration on this "original" type should contain all the relevant accessors.
You're not overwritting a method, you're overwritting a property definition
The default definition of the property includes Get/Set methods, and your new definition only includes a Get method, so it makes sense that your overwritten property only has Get available, not Set
Edit
If you run something like Reflector on this, you'll see that
class A
{
public virtual string TestString { get; set; }
}
class B : A
{
public override string TestString
{
get { return "x"; }
}
}
compiles into something like that looks like
internal class A
{
// Fields
[CompilerGenerated]
private string <TestString>k__BackingField;
// Methods
public A();
// Properties
public virtual string TestString { [CompilerGenerated] get; [CompilerGenerated] set; }
}
internal class B : A
{
// Methods
public B();
// Properties
public override string TestString { get; }
}
When you set the value in code, you are actually calling something like B.base.set_TestValue. When you reflect something, you are trying to find B.set_TestValue, which doesn't exist.
While true that you cannot overwrite a property, you can overwrite a property definition (providing it doesn't conflict with the base property definition). Since your question was originally tagged with WPF, I was thinking of DependencyProperties at the time, which are actually property definitions, and not properties in the sense that you might be thinking of.

Categories