GetProperties() возвращает все свойства иерархии наследования интерфейса
Предполагая следующую гипотетическую иерархию наследования:
public interface IA
{
int ID { get; set; }
}
public interface IB : IA
{
string Name { get; set; }
}
Используя отражение и делая следующий вызов:
typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance)
приведет только к свойствам интерфейса IB
, который равен "Name
".
Если бы мы выполнили аналогичный тест по следующему коду,
public abstract class A
{
public int ID { get; set; }
}
public class B : A
{
public string Name { get; set; }
}
вызов typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance)
вернет массив объектов PropertyInfo
для "ID
" и "Name
".
Есть ли простой способ найти все свойства в иерархии наследования для интерфейсов, как в первом примере?
Ответы
Ответ 1
Я изменил код примера @Marc Gravel в полезный метод расширения, который инкапсулирует как классы, так и интерфейсы. Он также добавляет свойства интерфейса, которые, по моему мнению, являются ожидаемым поведением.
public static PropertyInfo[] GetPublicProperties(this Type type)
{
if (type.IsInterface)
{
var propertyInfos = new List<PropertyInfo>();
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0)
{
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces())
{
if (considered.Contains(subInterface)) continue;
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var typeProperties = subType.GetProperties(
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance);
var newPropertyInfos = typeProperties
.Where(x => !propertyInfos.Contains(x));
propertyInfos.InsertRange(0, newPropertyInfos);
}
return propertyInfos.ToArray();
}
return type.GetProperties(BindingFlags.FlattenHierarchy
| BindingFlags.Public | BindingFlags.Instance);
}
Ответ 2
Type.GetInterfaces
возвращает сплющенную иерархию, поэтому нет необходимости в рекурсивном спуске.
Весь метод может быть написан гораздо более кратко с помощью LINQ:
public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type)
{
if (!type.IsInterface)
return type.GetProperties();
return (new Type[] { type })
.Concat(type.GetInterfaces())
.SelectMany(i => i.GetProperties());
}
Ответ 3
Иерархии интерфейсов - это боль - они на самом деле не "наследуют" как таковые, поскольку у вас может быть несколько "родителей" (из-за отсутствия лучшего термина).
"Сглаживание" (опять же, не совсем правильный термин), иерархия может включать проверку всех интерфейсов, которые интерфейс реализует и работает оттуда...
interface ILow { void Low();}
interface IFoo : ILow { void Foo();}
interface IBar { void Bar();}
interface ITest : IFoo, IBar { void Test();}
static class Program
{
static void Main()
{
List<Type> considered = new List<Type>();
Queue<Type> queue = new Queue<Type>();
considered.Add(typeof(ITest));
queue.Enqueue(typeof(ITest));
while (queue.Count > 0)
{
Type type = queue.Dequeue();
Console.WriteLine("Considering " + type.Name);
foreach (Type tmp in type.GetInterfaces())
{
if (!considered.Contains(tmp))
{
considered.Add(tmp);
queue.Enqueue(tmp);
}
}
foreach (var member in type.GetMembers())
{
Console.WriteLine(member.Name);
}
}
}
}
Ответ 4
Точно такая же проблема имеет обходное решение, описанное здесь.
FlattenHierarchy не работает кстати. (только на статических vars, говорит так в intellisense)
Обходной путь. Остерегайтесь дубликатов.
PropertyInfo[] pis = typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance);
Type[] tt = typeof(IB).GetInterfaces();
PropertyInfo[] pis2 = tt[0].GetProperties(BindingFlags.Public | BindingFlags.Instance);
Ответ 5
Отвечая на @douglas и @user3524983, следующее должно ответить на вопрос OP:
static public IEnumerable<PropertyInfo> GetPropertiesAndInterfaceProperties(this Type type, BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Instance)
{
if (!type.IsInterface) {
return type.GetProperties( bindingAttr);
}
return type.GetInterfaces().Union(new Type[] { type }).SelectMany(i => i.GetProperties(bindingAttr)).Distinct();
}
или, для индивидуального свойства:
static public PropertyInfo GetPropertyOrInterfaceProperty(this Type type, string propertyName, BindingFlags bindingAttr = BindingFlags.Public|BindingFlags.Instance)
{
if (!type.IsInterface) {
return type.GetProperty(propertyName, bindingAttr);
}
return type.GetInterfaces().Union(new Type[] { type }).Select(i => i.GetProperty( propertyName, bindingAttr)).Distinct().Where(propertyInfo => propertyInfo != null).Single();
}
ОК в следующий раз я отлажу его перед публикацией, а не после:-)
Ответ 6
это работало красиво и круто для меня в пользовательском MVC-образце. Должно быть, можно экстраполировать на любой сценарий отражения. Все еще воняет, что он тоже проходит
var props = bindingContext.ModelType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).ToList();
bindingContext.ModelType.GetInterfaces()
.ToList()
.ForEach(i => props.AddRange(i.GetProperties()));
foreach (var property in props)