다음과 같은 가상의 상속 계층을 가정합니다.
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
“에 대한 개체 배열을 반환합니다 .
첫 번째 예제에서와 같이 인터페이스에 대한 상속 계층 구조의 모든 속성을 쉽게 찾을 수있는 방법이 있습니까?
답변
@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);
}
답변
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());
}
답변
인터페이스 계층은 고통 스럽습니다. (더 나은 용어를 원하는 경우) 여러 “부모”를 가질 수 있기 때문에 실제로 “상속”되지 않습니다.
“편 평화”(다시 말하지만 올바른 용어는 아님) 계층 구조에는 인터페이스가 구현하고 거기에서 작동하는 모든 인터페이스를 검사하는 것이 포함될 수 있습니다.
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);
}
}
}
}
답변
정확히 동일한 문제에 여기에 설명 된 해결 방법이 있습니다 .
FlattenHierarchy가 btw 작동하지 않습니다. (정적 변수에서만. intellisense에서 그렇게 말합니다)
해결 방법. 중복에주의하십시오.
PropertyInfo[] pis = typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance);
Type[] tt = typeof(IB).GetInterfaces();
PropertyInfo[] pis2 = tt[0].GetProperties(BindingFlags.Public | BindingFlags.Instance);
답변
@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();
}
OK 다음에 게시하기 전에 디버깅하겠습니다 🙂
답변
이것은 사용자 정의 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)