리플렉션을 사용하여 선언 순서대로 속성 가져 오기
클래스에서 선언 된 순서대로 리플렉션을 사용하여 모든 속성을 가져와야합니다. MSDN에 따르면 사용시 주문이 보장되지 않습니다.GetProperties()
GetProperties 메서드는 사전 순 또는 선언 순서와 같은 특정 순서로 속성을 반환하지 않습니다.
하지만 .NET Core로 속성을 주문하는 방법이 있다는 것을 읽었습니다 MetadataToken
. 제 질문은 안전한가요? MSDN에 대한 정보를 찾을 수없는 것 같습니다. 아니면이 문제를 해결하는 다른 방법이 있습니까?
내 현재 구현은 다음과 같습니다.
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);
.net 4.5 (및 vs2012의 .net 4.0) 에서는 [CallerLineNumber]
속성이있는 영리한 트릭을 사용하여 리플렉션을 사용하여 훨씬 더 나은 작업을 수행 할 수 있으므로 컴파일러 가 속성에 순서를 삽입 할 수 있습니다.
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class OrderAttribute : Attribute
{
private readonly int order_;
public OrderAttribute([CallerLineNumber]int order = 0)
{
order_ = order;
}
public int Order { get { return order_; } }
}
public class Test
{
//This sets order_ field to current line number
[Order]
public int Property2 { get; set; }
//This sets order_ field to current line number
[Order]
public int Property1 { get; set; }
}
그런 다음 반사를 사용하십시오.
var properties = from property in typeof(Test).GetProperties()
where Attribute.IsDefined(property, typeof(OrderAttribute))
orderby ((OrderAttribute)property
.GetCustomAttributes(typeof(OrderAttribute), false)
.Single()).Order
select property;
foreach (var property in properties)
{
//
}
부분 클래스를 처리해야하는 경우를 사용하여 속성을 추가로 정렬 할 수 있습니다 [CallerFilePath]
.
MSDN 에 따르면 MetadataToken
하나의 모듈 내에서 고유합니다. 모든 주문을 보장한다는 말은 없습니다.
원하는 방식으로 작동하더라도 구현에 따라 다르며 예고없이 언제든지 변경 될 수 있습니다.
이 오래된 MSDN 블로그 항목을 참조하십시오 .
이러한 구현 세부 사항에 대한 종속성을 피하는 것이 좋습니다 . Marc Gravell의이 답변을 참조하십시오 .
컴파일 타임에 필요한 것이 있다면 Roslyn을 살펴볼 수 있습니다 (아주 초기 단계 임에도 불구하고).
속성 경로를 사용하는 경우 여기에 내가 과거에 사용한 방법이 있습니다.
public static IOrderedEnumerable<PropertyInfo> GetSortedProperties<T>()
{
return typeof(T)
.GetProperties()
.OrderBy(p => ((Order)p.GetCustomAttributes(typeof(Order), false)[0]).Order);
}
그런 다음 이렇게 사용하십시오.
var test = new TestRecord { A = 1, B = 2, C = 3 };
foreach (var prop in GetSortedProperties<TestRecord>())
{
Console.WriteLine(prop.GetValue(test, null));
}
어디;
class TestRecord
{
[Order(1)]
public int A { get; set; }
[Order(2)]
public int B { get; set; }
[Order(3)]
public int C { get; set; }
}
모든 속성에 대해 비교 가능한 속성이없는 유형에서 실행하는 경우 메서드가 작동하지 않을 수 있으므로 사용 방법에주의하고 요구 사항에 충분해야합니다.
나는 주문 : 속성의 정의를 생략했다. Yahia의 Marc Gravell의 포스트에 대한 좋은 샘플이 있기 때문이다.
내가 MetadataToken으로 정렬을 테스트 한 것은 작동합니다.
여기에있는 사용자 중 일부는 이것이 어떻게 든 좋은 접근 방식이 아니거나 신뢰할 수 없다고 주장하지만 아직 그 증거를 보지 못했습니다. 주어진 접근 방식이 작동하지 않을 때 여기에 코드 스 니펫을 게시 할 수 있습니까?
이전 버전과의 호환성에 대해-현재 .net 4 / .net 4.5에서 작업하는 동안-Microsoft는 .net 5 이상을 만들고 있으므로이 정렬 방법이 향후에 손상되지 않을 것이라고 예상 할 수 있습니다.
물론 2017 년이되면 .net9로 업그레이드 할 때 호환성이 중단 될 것입니다.하지만 그때 쯤이면 Microsoft 직원이 "공식 정렬 메커니즘"을 알아낼 것입니다. 돌아가거나 물건을 부수는 것은 의미가 없습니다.
속성 순서 지정을위한 추가 속성을 사용하는 것도 시간과 구현이 필요합니다. MetadataToken 정렬이 작동하면 왜 귀찮게해야합니까?
사용자 지정 특성 대신 System.Component.DataAnnotations에서 DisplayAttribute를 사용할 수 있습니다. 어쨌든 당신의 요구 사항은 디스플레이에 뭔가를해야합니다.
추가 종속성에 만족한다면 Marc Gravell의 Protobuf-Net 을 사용하여 리플렉션 및 캐싱 등을 구현하는 가장 좋은 방법에 대해 걱정할 필요없이이 작업을 수행 할 수 있습니다. [ProtoMember]
다음을 사용하여 필드를 장식 한 다음 다음을 사용하여 필드에 액세스합니다.
MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)];
metaData.GetFields();
나는 이렇게했다 :
internal static IEnumerable<Tuple<int,Type>> TypeHierarchy(this Type type)
{
var ct = type;
var cl = 0;
while (ct != null)
{
yield return new Tuple<int, Type>(cl,ct);
ct = ct.BaseType;
cl++;
}
}
internal class PropertyInfoComparer : EqualityComparer<PropertyInfo>
{
public override bool Equals(PropertyInfo x, PropertyInfo y)
{
var equals= x.Name.Equals(y.Name);
return equals;
}
public override int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
internal static IEnumerable<PropertyInfo> GetRLPMembers(this Type type)
{
return type
.TypeHierarchy()
.SelectMany(t =>
t.Item2
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(prop => Attribute.IsDefined(prop, typeof(RLPAttribute)))
.Select(
pi=>new Tuple<int,PropertyInfo>(t.Item1,pi)
)
)
.OrderByDescending(t => t.Item1)
.ThenBy(t => t.Item2.GetCustomAttribute<RLPAttribute>().Order)
.Select(p=>p.Item2)
.Distinct(new PropertyInfoComparer());
}
with the property declared as follows:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RLPAttribute : Attribute
{
private readonly int order_;
public RLPAttribute([CallerLineNumber]int order = 0)
{
order_ = order;
}
public int Order { get { return order_; } }
}
Building on the above accepted solution, to get the exact Index you could use something like this
Given
public class MyClass
{
[Order] public string String1 { get; set; }
[Order] public string String2 { get; set; }
[Order] public string String3 { get; set; }
[Order] public string String4 { get; set; }
}
Extensions
public static class Extensions
{
public static int GetOrder<T,TProp>(this T Class, Expression<Func<T,TProp>> propertySelector)
{
var body = (MemberExpression)propertySelector.Body;
var propertyInfo = (PropertyInfo)body.Member;
return propertyInfo.Order<T>();
}
public static int Order<T>(this PropertyInfo propertyInfo)
{
return typeof(T).GetProperties()
.Where(property => Attribute.IsDefined(property, typeof(OrderAttribute)))
.OrderBy(property => property.GetCustomAttributes<OrderAttribute>().Single().Order)
.ToList()
.IndexOf(propertyInfo);
}
}
Usage
var myClass = new MyClass();
var index = myClass.GetOrder(c => c.String2);
Note, there is no error checking or fault tolerance, you can add pepper and salt to taste
참고URL : https://stackoverflow.com/questions/9062235/get-properties-in-order-of-declaration-using-reflection
'developer tip' 카테고리의 다른 글
Chrome, Safari가 표의 최대 너비를 무시 함 (0) | 2020.10.26 |
---|---|
ANTLR 3.3을 사용하십니까? (0) | 2020.10.26 |
메인 콘텐츠 div를 CSS로 화면 높이를 채우는 방법 (0) | 2020.10.25 |
인스턴스 수준에서 메서드 재정의 (0) | 2020.10.25 |
LR (0)과 SLR 구문 분석의 차이점은 무엇입니까? (0) | 2020.10.25 |