Is it possible to discover all dynamic resources within a datatemplate - either within the datatemplate itself, or after it was applied to some ContentPresenter?
My idea was to make some sort of property editor to edit the appearance of wpf objects (maybe dynamically created using XamlReader) and show - for a certain object, only the resource entries used inside the corresponding DataTemplate.
Please look at Snoop utility. You can look at the source code and see how you can view the style/template of any object and change its appearance.
Up to now, I have not found a "clean" way to get the dynamic resource using framework methods.
However, one possibility is to search the DataTemplate using reflection. The dynamic resources are stored inside structs of type System.Windows.ChildValueLookup where the LookupType is "Resource". I have written a helper class to enumerate the resources:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
namespace DashboardTest
{
public struct ResourceInfo
{
public ResourceInfo(string resourceKey, DependencyProperty property)
: this()
{
ResourceKey = resourceKey;
Property = property;
}
public string ResourceKey { get; private set; }
public DependencyProperty Property { get; set; }
}
public static class DataTemplateHelper
{
private static readonly Type ChildValueLookupType = GetType("System.Windows.ChildValueLookup");
private static readonly FieldInfo LookupTypeField = ChildValueLookupType.GetField("LookupType", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo ValueField = ChildValueLookupType.GetField("Value", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo PropertyField = ChildValueLookupType.GetField("Property", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly object LookupTypeResource = Enum.Parse(LookupTypeField.FieldType, "Resource");
public static List<ResourceInfo> FindDynamicResources(DataTemplate template)
{
var recordField = typeof(DataTemplate).GetField("ChildRecordFromChildIndex", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(recordField != null);
var values = EnumerateObjects(recordField.GetValue(template)).Where(IsDynamicResource).Select(ToResourceInfo).ToList();
return values;
}
private static ResourceInfo ToResourceInfo(object lookup)
{
return new ResourceInfo((string)ValueField.GetValue(lookup), (DependencyProperty)PropertyField.GetValue(lookup));
}
private static Type GetType(string typeName)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Type type = assembly.GetType(typeName);
if (type != null)
return type;
}
return null;
}
private static bool IsDynamicResource(object obj)
{
if (obj == null || obj.GetType() != ChildValueLookupType) return false;
return LookupTypeResource.Equals(LookupTypeField.GetValue(obj));
}
private static IEnumerable<object> EnumerateObjects(object obj)
{
var visited = new HashSet<object>();
EnumerateObjectsInternal(obj, visited, 20);
return visited;
}
private static void EnumerateObjectsInternal(object obj, HashSet<object> visited, int maxDepth)
{
if (obj == null || maxDepth <= 0) return;
var type = obj.GetType();
if (obj is Type || obj is string || obj is DateTime || type.IsPrimitive
|| type.IsEnum || visited.Add(obj) == false) return;
var array = obj as Array;
if (array != null)
{
foreach (var item in array)
{
EnumerateObjectsInternal(item, visited, maxDepth - 1);
}
}
else
{
foreach (var field in GetAllFields(type))
{
var fieldValue = field.GetValue(obj);
EnumerateObjectsInternal(fieldValue, visited, maxDepth - 1);
}
}
}
private static IEnumerable<FieldInfo> GetAllFields(Type type)
{
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
if (type == typeof(object) || type.BaseType == typeof(object))
{
return type.GetFields(bindingFlags);
}
else
{
var fieldInfoList = new List<FieldInfo>();
var currentType = type;
while (currentType != typeof(object))
{
fieldInfoList.AddRange(currentType.GetFields(bindingFlags));
currentType = currentType.BaseType;
}
return fieldInfoList;
}
}
}
}
Related
I have a LookUpEdit control and I need set property value to NullText with reflection, but I'm getting the TargetException:
private static void SetObjectProperty(string propiedad, string valor, object obj)
{
if (obj.GetType() == typeof(LookUpEdit))
{
string[] vv = propiedad.Split('.');
string prop = vv[0];
string propType = vv[1];
var p = obj.GetType().GetProperty(prop, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
PropertyInfo propertyInfo = p.PropertyType.GetProperty(propType);
if (propertyInfo != null)
{
propertyInfo.SetValue(obj, valor, null);
}
}
}
I only get the exception with LookUpEdit control.
"propiedad" is a string contains "Properties.NullText" so this is why I'm doing a split
You should apply operations with nested properties to corresponding nested objects:
static void SetObjectProperty(object obj, string propertyPath, object value) {
if(obj != null && obj.GetType() == typeof(LookUpEdit)) {
string[] parts = propertyPath.Split('.');
var rootInfo = typeof(LookUpEdit).GetProperty(parts[0],
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
object root = rootInfo.GetValue(obj); // obtaining a root
var nestedInfo = rootInfo.PropertyType.GetProperty(parts[1]);
if(nestedInfo != null)
nestedInfo.SetValue(root, value, null); // using root object
}
}
PS. Why you're using this ugly way of modifying object properties?
I'm looking to do something like this:
string currPanel = "Panel";
currPanel += ".Visible"
Now at this point I have a string variable with the name of a property that only accepts Boolean values. Can I some how do something like this:
<data type> currPanel = true;
so the actual property Panel1.Visible accepts it without any errors?
Supporting both properties and fields, but only instance ones:
public static void SetValue(object obj, string name, object value)
{
string[] parts = name.Split('.');
if (parts.Length == 0)
{
throw new ArgumentException("name");
}
PropertyInfo property = null;
FieldInfo field = null;
object current = obj;
for (int i = 0; i < parts.Length; i++)
{
if (current == null)
{
throw new ArgumentNullException("obj");
}
string part = parts[i];
Type type = current.GetType();
property = type.GetProperty(part, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (property != null)
{
field = null;
if (i + 1 != parts.Length)
{
current = property.GetValue(current);
}
continue;
}
field = type.GetField(part, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
property = null;
if (i + 1 != parts.Length)
{
current = field.GetValue(current);
}
continue;
}
throw new ArgumentException("name");
}
if (current == null)
{
throw new ArgumentNullException("obj");
}
if (property != null)
{
property.SetValue(current, value);
}
else if (field != null)
{
field.SetValue(current, value);
}
}
example of use:
public class Panel
{
public bool Visible { get; set; }
}
public class MyTest
{
public Panel Panel1 = new Panel();
public void Do()
{
string currPanel = "Panel1";
currPanel += ".Visible";
SetValue(this, currPanel, true);
}
}
and
var mytest = new MyTest();
mytest.Do();
Note that I'm not supporting indexers (like Panel1[5].Something). Supporting int indexers would be feasible (but another 30 lines of code). Supporting not-int indexers (like ["Hello"]) or multi-key indexers (like [1, 2]) would be quite hard.
From what I can see here is that you are using WPF. Things like this you accomplish with converters
http://www.codeproject.com/Tips/285358/All-purpose-Boolean-to-Visibility-Converter
With MVVM and WPF you never need to do this
I am trying to set a value to a Nested Property of Class dynamically using reflection. Could anyone help me to do this.
I am having a class Region like below.
public class Region
{
public int id;
public string name;
public Country CountryInfo;
}
public class Country
{
public int id;
public string name;
}
I have a Oracle Data reader to provide the Values from the Ref cursor.
which will give me as
Id,name,Country_id,Country_name
I could able to assign the values to the Region.Id, Region.Name by below.
FieldName="id"
prop = objItem.GetType().GetProperty(FieldName, BindingFlags.Public | BindingFlags.Instance);
prop.SetValue(objItem, Utility.ToLong(reader_new[ResultName]), null);
And for the Nested Property I could able to do the assign values to the as below by creating a Instance by reading the Fieldname.
FieldName="CountryInfo.id"
if (FieldName.Contains('.'))
{
Object NestedObject = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
//getting the Type of NestedObject
Type NestedObjectType = NestedObject.GetType();
//Creating Instance
Object Nested = Activator.CreateInstance(typeNew);
//Getting the nested Property
PropertyInfo nestedpropinfo = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] nestedpropertyInfoArray = nestedpropinfo.PropertyType.GetProperties();
prop = nestedpropertyInfoArray.Where(p => p.Name == Utility.Trim(FieldName.Split('.')[1])).SingleOrDefault();
prop.SetValue(Nested, Utility.ToLong(reader_new[ResultName]), null);
Nestedprop = objItem.GetType().GetProperty(Utility.Trim(FieldName.Split('.')[0]), BindingFlags.Public | BindingFlags.Instance);
Nestedprop.SetValue(objItem, Nested, null);
}
The above assign values to Country.Id.
But Since I am creating instance each and every time I could not able to get the previous Country.Id value if I go for the Next Country.Name.
Could anybody tell could to assign values to the objItem(that is Region).Country.Id and objItem.Country.Name. Which means how to assign values to the Nested Properties instead of creating instance and assigning everytime.
Thanks in advance.!
You should be calling PropertyInfo.GetValue using the Country property to get the country, then PropertyInfo.SetValue using the Id property to set the ID on the country.
So something like this:
public void SetProperty(string compoundProperty, object target, object value)
{
string[] bits = compoundProperty.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]);
target = propertyToGet.GetValue(target, null);
}
PropertyInfo propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
Expanding on Jon Skeets answer, if the value you're getting is null and you want to create an object for that type:
public void SetProperty(string compoundProperty, object target, object value)
{
var bits = compoundProperty.Split('.');
for (var i = 0; i < bits.Length - 1; i++) {
var propertyToGet = target.GetType().GetProperty(bits[i]);
var propertyValue = propertyToGet.GetValue(target, null);
if (propertyValue == null) {
propertyValue = Activator.CreateInstance(propertyToGet.PropertyType);
propertyToGet.SetValue(target, propertyValue);
}
target = propertyToGet.GetValue(target, null);
}
var propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
Expanding on Luke Garrigan Answer:
If you want to have it working also with non lists too:
public void SetProperty(string compoundProperty, object target, object value)
{
var bits = compoundProperty.Split('.');
if(bits == null) bits = new string[1]{compoundProperty};
for (var i = 0; i < bits.Length - 1; i++) {
var propertyToGet = target.GetType().GetProperty(bits[i]);
var propertyValue = propertyToGet.GetValue(target, null);
if (propertyValue == null) {
propertyValue = Activator.CreateInstance(propertyToGet.PropertyType);
propertyToGet.SetValue(target, propertyValue);
}
target = propertyToGet.GetValue(target, null);
}
var propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
I try to get a list of all methods of a type. Type provides the GetMethods method to do this. But unfortunately it seems to be incorrectly implemented. It works properly as long as there is no overridden generic method on the reflected type. In this special case a MethodAccessException is thrown.
Does anyone have a workaround for this WP7 bug? I'm fine if all methods except the generic ones are returned.
Here is a sample of a class that will throw an exception. Note: the none generic return value is intended to prove that the return value is not involved in the problem. Furthermore, the base method can be changed to abstract and the problem still remains.
public abstract class BaseClassWithGenericMethod
{
public virtual System.Collections.IList CreateList<T>()
{
return new List<T>();
}
}
public class DerivedClassWithGenericMethod
: BaseClassWithGenericMethod
{
public override System.Collections.IList CreateList<T>()
{
return new List<T>();
}
}
I'd grab them all (note the BindingFlags.DeclaredOnly in GetMethods) and then filter out those that are generic methods (MethodInfo.IsGenericMethod).
Sorry for the VB, I know the whole world wants C# these days, but...
Public Function GetListOfMethods() As List(Of MethodInfo)
Dim d As New DerivedClassWithGenericMethod
Dim myArrayMethodInfo() As Reflection.MethodInfo
myArrayMethodInfo = d.GetType.GetMethods(BindingFlags.Instance _
Or BindingFlags.Public _
Or BindingFlags.DeclaredOnly)
Dim myArrayMethodInfoList As New List(Of MethodInfo)
For Each m As MethodInfo In myArrayMethodInfo
If Not m.IsGenericMethod Then
myArrayMethodInfoList.Add(m)
End If
Next
Return myArrayMethodInfoList
End Function
I just tested on WP7 using your sample classes and it works fine.
Finally I got it working. The following Type extension methods return exactly the same result as the .NET 4.0 implementation but without the MethodAccess exceptions thrown by WP7:
public static class TypeExtensions
{
public static MethodInfo GetMethodWp7Workaround(this Type type, string name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
return GetMethod(type, name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Any, null, null);
}
public static MethodInfo GetMethodWp7Workaround(this Type type, string name, Type[] types)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (types == null)
{
throw new ArgumentNullException("types");
}
if (types.Any(t => t == null))
{
throw new ArgumentNullException("types");
}
return GetMethod(type, name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Any, types, null);
}
public static MethodInfo GetMethodWp7Workaround(this Type type, string name, BindingFlags bindingAttr)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
return GetMethod(type, name, bindingAttr, null, CallingConventions.Any, null, null);
}
public static MethodInfo GetMethodWp7Workaround(this Type type, string name, Type[] types, ParameterModifier[] modifiers)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (types == null)
{
throw new ArgumentNullException("types");
}
if (types.Any(t => t == null))
{
throw new ArgumentNullException("types");
}
return GetMethod(type, name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Any, types, modifiers);
}
public static MethodInfo GetMethodWp7Workaround(this Type type, string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (types == null)
{
throw new ArgumentNullException("types");
}
if (types.Any(t => t == null))
{
throw new ArgumentNullException("types");
}
return GetMethod(type, name, bindingAttr, binder, CallingConventions.Any, types, modifiers);
}
public static MethodInfo GetMethodWp7Workaround(this Type type, string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (types == null)
{
throw new ArgumentNullException("types");
}
if (types.Any(t => t == null))
{
throw new ArgumentNullException("types");
}
return GetMethod(type, name, bindingAttr, binder, callConvention, types, modifiers);
}
private static MethodInfo GetMethod(
Type type,
string name,
BindingFlags bindingFlags,
Binder binder,
CallingConventions callConvention,
Type[] types,
ParameterModifier[] modifiers)
{
if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly)
{
return types == null
? type.GetMethod(name, bindingFlags)
: type.GetMethod(name, bindingFlags, binder, callConvention, types, modifiers);
}
bool isBaseType = false;
bindingFlags = bindingFlags | BindingFlags.DeclaredOnly;
MethodInfo result = null;
while (result == null && type != null)
{
result =
types == null
? type.GetMethod(name, bindingFlags)
: type.GetMethod(name, bindingFlags, binder, callConvention, types, modifiers);
if (isBaseType && result != null && result.IsPrivate)
{
result = null;
}
type = type.BaseType;
if (!isBaseType)
{
isBaseType = true;
bindingFlags = bindingFlags & (~BindingFlags.Static);
}
}
return result;
}
public static MethodInfo[] GetMethodsWp7Workaround(this Type type)
{
return type.GetMethodsWp7Workaround(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
}
public static MethodInfo[] GetMethodsWp7Workaround(this Type type, BindingFlags flags)
{
if ((flags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly)
{
return type.GetMethods(flags);
}
flags = flags | BindingFlags.DeclaredOnly;
Type currentType = type;
bool isBaseType = false;
var methods = new List<MethodInfo>();
while (currentType != null)
{
var newMethods = currentType.GetMethods(flags).Where(m => ShouldBeReturned(m, methods, isBaseType));
methods.AddRange(newMethods);
currentType = currentType.BaseType;
if (!isBaseType)
{
isBaseType = true;
flags = flags & (~BindingFlags.Static);
}
}
return methods.ToArray();
}
private static bool ShouldBeReturned(
MethodInfo method,
IEnumerable<MethodInfo> foundMethods,
bool isCurrentTypeBaseType)
{
return !isCurrentTypeBaseType || (!method.IsPrivate && !HasAlreadyBeenFound(method, foundMethods));
}
private static bool HasAlreadyBeenFound(
MethodInfo method,
IEnumerable<MethodInfo> processedMethods)
{
if (!method.IsGenericMethodDefinition)
{
return processedMethods.Any(m => m.GetBaseDefinition().Equals(method.GetBaseDefinition()));
}
return processedMethods.Any(
m => m.Name == method.Name &&
HaveSameGenericArguments(m, method) &&
HaveSameParameters(m, method));
}
private static bool HaveSameParameters(MethodInfo method1, MethodInfo method2)
{
var parameters1 = method1.GetParameters();
var parameters2 = method2.GetParameters();
return parameters1.Length == parameters2.Length &&
parameters1.All(parameters2.Contains);
}
private static bool HaveSameGenericArguments(MethodInfo method1, MethodInfo method2)
{
var genericArguments1 = method1.GetGenericArguments();
var genericArguments2 = method2.GetGenericArguments();
return genericArguments1.Length == genericArguments2.Length;
}
}
You will get the same result for the following class with .NET 4.0 and this workaround on WP7:
internal class TestBaseClass
{
private static void BasePrivateStaticMethod() { }
private void BasePrivateMethod() { }
private void BasePrivateGenericMethod<T>() { }
protected static void BaseProtectedStaticMethod() { }
protected void BaseProtectedMethod() { }
protected void BaseProtectedGenericMethod<T>() { }
protected virtual void OverriddenProtectedMethod() { }
protected virtual void OverriddenProtectedGenericMethod<T>() { }
internal static void BaseInternalStaticMethod() { }
internal void BaseInternalMethod() { }
internal void BaseInternalGenericMethod<T>() { }
internal virtual void OverriddenInternalMethod() { }
internal virtual void OverriddenInternalGenericMethod<T>() { }
public static void BasePublicStaticMethod() { }
public void BasePublicMethod() { }
public void BasePublicGenericMethod<T>() { }
public virtual void OverriddenPublicMethod() { }
public virtual void OverriddenPublicGenericMethod<T>() { }
}
internal class TestClass : TestBaseClass
{
public string Property
{
get { return null; }
}
private static void PrivateStaticMethod() { }
private void PrivateMethod() { }
private void PrivateGenericMethod<T>() { }
protected static void ProtectedStaticMethod() { }
protected void ProtectedMethod() { }
protected static void ProtectedGenericMethod<T>() { }
internal static void InternalGenericMethod<T>() { }
internal void InternalMethod() { }
internal static void InternalStaticMethod() { }
public static void PublicStaticMethod() { }
public void PublicMethod() { }
public static void PublicGenericMethod<T>() { }
internal override void OverriddenInternalMethod()
{
base.OverriddenInternalMethod();
}
protected override void OverriddenProtectedMethod()
{
base.OverriddenProtectedMethod();
}
public override void OverriddenPublicMethod()
{
base.OverriddenPublicMethod();
}
internal override void OverriddenInternalGenericMethod<T>()
{
base.OverriddenInternalGenericMethod<T>();
}
protected override void OverriddenProtectedGenericMethod<T>()
{
base.OverriddenProtectedGenericMethod<T>();
}
public override void OverriddenPublicGenericMethod<T>()
{
base.OverriddenPublicGenericMethod<T>();
}
}
Could you actually be misinterpreting the behavior you are seeing? I am thinking you are experiencing a valid security access issue and nothing to do with the implementation of reflection on WP7 (other than its security model).
Look at this post. Could the type you are reflecting, any of the methods in question or the Type assigned to T in your particular case be marked as security critical with the SecurityCriticalAttribute?
I gotta admit that I doubted what you are reporting but it is indeed easily reproducible and does look like a bug to me. I think you should report it on Microsoft connect.
As for work arounds...
This will get you the members of the derived class and then the base class (including the generic) without error:
var m = new DerivedClassWithGenericMethod();
foreach (var method in m.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
Debug.WriteLine(method.Name);
foreach (var method in m.GetType().BaseType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
Debug.WriteLine(method.Name);
This can be generalized (quick and dirty implementation) somewhat like so (The EqualityComprarer should filter out base class members that are overriden or hidden by derived classes):
class MethodComparer : IEqualityComparer<MethodInfo>
{
public bool Equals(MethodInfo x, MethodInfo y)
{
return GetHashCode(x) == GetHashCode(y);
}
public int GetHashCode(MethodInfo obj)
{
int hash = obj.Name.GetHashCode();
foreach (var param in obj.GetParameters())
hash ^= param.ParameterType.GetHashCode();
if (obj.IsGenericMethodDefinition)
{
hash ^= obj.GetGenericArguments().Length.GetHashCode();
}
else if (obj.IsGenericMethod)
{
foreach (var t in obj.GetGenericArguments())
hash ^= t.GetHashCode();
}
return hash;
}
}
static class Ext
{
public static MethodInfo[] MyGetMethods(this Type t)
{
if (t == null)
return new MethodInfo[] { };
var methods = t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
var baseMethods = from m in t.BaseType.MyGetMethods()
where !methods.Contains(m, new MethodComparer())
select m;
return methods.Concat(baseMethods).ToArray();
}
}
var m = new DerivedClassWithGenericMethod();
foreach (var method in m.GetType().MyGetMethods())
Debug.WriteLine(method.Name);
Theres a GetMethods(BindingFlags). Try filtering out the methods you are getting with the BindingFlags argument.
More specifically, if I have:
public class TempClass : TempInterface
{
int TempInterface.TempProperty
{
get;
set;
}
int TempInterface.TempProperty2
{
get;
set;
}
public int TempProperty
{
get;
set;
}
}
public interface TempInterface
{
int TempProperty
{
get;
set;
}
int TempProperty2
{
get;
set;
}
}
How do I use reflection to get all the propertyInfos for properties explicitly implementing TempInterface?
Thanks.
I think the class you are looking for is System.Reflection.InterfaceMapping.
Type ifaceType = typeof(TempInterface);
Type tempType = typeof(TempClass);
InterfaceMapping map = tempType.GetInterfaceMap(ifaceType);
for (int i = 0; i < map.InterfaceMethods.Length; i++)
{
MethodInfo ifaceMethod = map.InterfaceMethods[i];
MethodInfo targetMethod = map.TargetMethods[i];
Debug.WriteLine(String.Format("{0} maps to {1}", ifaceMethod, targetMethod));
}
The property getter and setter of an explicitly implemented interface property has an unusual attribute. It's IsFinal property is True, even when it is not a member of a sealed class. Try this code to verify my assertion:
foreach (AssemblyName name in Assembly.GetEntryAssembly().GetReferencedAssemblies()) {
Assembly asm = Assembly.Load(name);
foreach (Type t in asm.GetTypes()) {
if (t.IsAbstract) continue;
foreach (MethodInfo mi in t.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) {
int dot = mi.Name.LastIndexOf('.');
string s = mi.Name.Substring(dot + 1);
if (!s.StartsWith("get_") && !s.StartsWith("set_")) continue;
if (mi.IsFinal)
Console.WriteLine(mi.Name);
}
}
}
Here's a modified solution based on the implementation given in this blog post:
var explicitProperties =
from prop in typeof(TempClass).GetProperties(
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
let getAccessor = prop.GetGetMethod(true)
where getAccessor.IsFinal && getAccessor.IsPrivate
select prop;
foreach (var p in explicitProperties)
Console.WriteLine(p.Name);
Building on the answer by MrKurt:
var targetMethods =
from iface in typeof(TempClass).GetInterfaces()
from method in typeof(TempClass).GetInterfaceMap(iface).TargetMethods
select method;
var explicitProps =
from prop in typeof(TempClass).GetProperties(BindingFlags.Instance |
BindingFlags.NonPublic)
where targetMethods.Contains(prop.GetGetMethod(true)) ||
targetMethods.Contains(prop.GetSetMethod(true))
select prop;
I had to modify Jacob Carpenter's answer but it works nicely. nobugz's also works but Jacobs is more compact.
var explicitProperties =
from method in typeof(TempClass).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
where method.IsFinal && method.IsPrivate
select method;
A simple helper class that could help:
public class InterfacesPropertiesMap
{
private readonly Dictionary<Type, PropertyInfo[]> map;
public InterfacesPropertiesMap(Type type)
{
this.Interfaces = type.GetInterfaces();
var properties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
this.map = new Dictionary<Type, PropertyInfo[]>(this.Interfaces.Length);
foreach (var intr in this.Interfaces)
{
var interfaceMap = type.GetInterfaceMap(intr);
this.map.Add(intr, properties.Where(p => interfaceMap.TargetMethods
.Any(t => t == p.GetGetMethod(true) ||
t == p.GetSetMethod(true)))
.Distinct().ToArray());
}
}
public Type[] Interfaces { get; private set; }
public PropertyInfo[] this[Type interfaceType]
{
get { return this.map[interfaceType]; }
}
}
You'll get properties for each interface, even explicitly implemented.
It's overly complex. You have to reflect over the methods/properties of the Interface type, see if they exist in your class type, and compare them to see if they're the "same" when they do exist.
If something is in the interface but not the type you're testing, it's an explicit implementation. If it's in both, but different between the two, it's an explicit interface.
This seems a bit painful for no apparent reason!
My solution is for the case where you know the name of the property you are looking for and is pretty simple.
I have a class for making reflection a bit easier that I just had to add this case to:
public class PropertyInfoWrapper
{
private readonly object _parent;
private readonly PropertyInfo _property;
public PropertyInfoWrapper(object parent, string propertyToChange)
{
var type = parent.GetType();
var privateProperties= type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
var property = type.GetProperty(propertyToChange) ??
privateProperties.FirstOrDefault(p => UnQualifiedNameFor(p) == propertyName);
if (property == null)
throw new Exception(string.Format("cant find property |{0}|", propertyToChange));
_parent = parent;
_property = property;
}
private static string UnQualifiedNameFor(PropertyInfo p)
{
return p.Name.Split('.').Last();
}
public object Value
{
get { return _property.GetValue(_parent, null); }
set { _property.SetValue(_parent, value, null); }
}
}
You cant just do == on name because explicitly implemented properties have fully qualified names.
GetProperties needs both the search flags to get at private properties.
Jacob's code is missing a filter:
var props = typeof(TempClass).GetInterfaces().Where(i => i.Name=="TempInterface").SelectMany(i => i.GetProperties());
foreach (var prop in props)
Console.WriteLine(prop);