클래스에서 선언 된 순서대로 리플렉션을 사용하여 모든 속성을 가져와야합니다. 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]
.
답변
속성 경로를 사용하는 경우 여기에 내가 과거에 사용한 방법이 있습니다.
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의 포스트에 대한 링크에 좋은 샘플이 있기 때문이다.
답변
MSDN 에 따르면 MetadataToken
하나의 모듈 내에서 고유합니다. 모든 주문을 보장한다는 말은 없습니다.
원하는 방식으로 작동하더라도 구현에 따라 다르며 예고없이 언제든지 변경 될 수 있습니다.
이 오래된 MSDN 블로그 항목을 참조하십시오 .
이러한 구현 세부 사항에 대한 종속성을 피하는 것이 좋습니다 . Marc Gravell의이 답변을 참조하십시오 .
컴파일 타임에 필요한 것이 있다면 Roslyn을 살펴볼 수 있습니다 (아주 초기 단계 임에도 불구하고).
답변
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());
}
다음과 같이 선언 된 속성으로 :
[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_; } }
}