प्रतिबिंब का उपयोग करके घोषणा के क्रम में गुण प्राप्त करें


81

मुझे कक्षा में जिस क्रम में घोषित किया गया है, उसमें प्रतिबिंब का उपयोग करके सभी गुण प्राप्त करने की आवश्यकता है। MSDN के अनुसार उपयोग करते समय आदेश की गारंटी नहीं दी जा सकती हैGetProperties()

GetProperties विधि एक विशेष क्रम में गुण नहीं लौटाती है, जैसे कि वर्णमाला या घोषणा क्रम।

लेकिन मैंने पढ़ा है कि गुणों को क्रम में रखकर वर्कअराउंड है MetadataToken। तो मेरा सवाल यह है कि क्या यह सुरक्षित है? मुझे लगता है कि इसके बारे में MSDN पर कोई जानकारी नहीं मिल सकती। या इस समस्या को सुलझाने का कोई और तरीका है?

मेरा वर्तमान कार्यान्वयन निम्नानुसार है:

var props = typeof(T)
   .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
   .OrderBy(x => x.MetadataToken);

12
वैसे भी, यह बुरा विचार है। ऑर्डर मूल्य या किसी अन्य मेटाडेटा के साथ अपनी विशेषता बनाएं और उस विशेषता के साथ वर्ग के फ़ील्ड चिह्नित करें।
किरिल पोलिशचुक

1
आप शायद एक नई विशेषता जोड़ सकते हैं जिसमें आदेश का एक उदाहरण है। फिर गुण प्राप्त करें, प्रत्येक गुण का प्रदर्शन प्राप्त करें।
ब्लूचिप्पी

1
जिज्ञासा से बाहर, आप ऐसा क्यों कर रहे हैं, आप क्या हासिल करने की कोशिश कर रहे हैं?
सैम ग्रीनहाल

6
@ मैग्नस फिर भी सवाल अभी भी एक दिलचस्प है क्योंकि फ्रेमवर्क के कुछ हिस्से खुद इस पर बहुत अधिक निर्भर हैं। उदाहरण के लिए अनुक्रमिक विशेषता वाले क्रमांकन सदस्यों को उस क्रम में संग्रहीत करते हैं, जिस क्रम में उन्हें परिभाषित किया गया था। लेग वैगनर ने अपनी पुस्तक "इफेक्टिव सी #" में यह कहा है
पावेल वोरोनिन

जवाबों:


145

.Net 4.5 पर (और यहां तक ​​कि .net 4.0 vs2012 में) आप चालाक चाल का उपयोग करके प्रतिबिंब के साथ बहुत बेहतर कर सकते हैं[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]


2
यह वास्तव में बहुत चालाक है! मुझे आश्चर्य है कि अगर इसके खिलाफ कोई काउंटर तर्क है? वास्तव में मेरे लिए काफी सुरुचिपूर्ण प्रतीत होता है। मैं Linq2CSV का उपयोग कर रहा हूं और मुझे लगता है कि मैं इनहेरिट करूंगा CsvColumnAttributeऔर इसे डिफ़ॉल्ट FieldIndexमान के रूप में उपयोग
करूंगा

2
@julealgon मैं मानती हूं कि इस के खिलाफ तर्क यह है कि एक अनिर्दिष्ट, स्थितीय एपीआई है जिसे कोई रिफैक्टिंग करते समय तोड़ सकता है यदि वे यह नहीं समझते कि इस शैली में विशेषता का उपयोग किया जा रहा था। मुझे अभी भी लगता है कि यह बहुत सुंदर है, अगर कोई इसे कॉपी / पेस्ट करता है, तो अगले लड़के के लिए कुछ टिप्पणियां छोड़ने के लिए प्रेरणा की तलाश में है।
जोएल बी

2
यह उस मामले को छोड़कर काम करता है जहां 1 वर्ग को दूसरे वर्ग की विरासत मिलती है और मुझे सभी संपत्तियों के क्रम की आवश्यकता होती है। क्या उसके लिए भी कोई तरकीब है?
पॉल बैक्सटर

मुझे लगता है कि अगर वर्ग आंशिक घोषित किया जाता है तो यह टूट जाएगा। विरासत के विपरीत, एपीआई यह पता नहीं लगा सकता है।
वू जेनवेई

1
बहुत चालाक दृष्टिकोण लेकिन यह अशक्त संदर्भ अपवाद को फेंक देगा यदि कुछ लोग सेट [आदेश] विशेषता को भूल जाते हैं। कॉल करने से पहले नल की जांच करना बेहतर है। पूर्व: var गुण = टाइपऑफ़ (टेस्ट) में संपत्ति से .GetProperties () आज्ञा दें ऑर्डर करें = (property.GetCustomAttributes (टाइपऑफ़ट्रेड), गलत)। (आर्डरअवितरण), असत्य) .सिंगलऑर्डीफॉल्ट ()) आर्डर आर्डर ऑर्डर के रूप में।
सोपान मैती

15

यदि आप विशेषता मार्ग पर जा रहे हैं, तो यहां एक विधि है जिसका मैंने अतीत में उपयोग किया है;

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; }
}

यदि आप इसे स्पष्ट रूप से अपने सभी गुणों पर तुलनीय विशेषताओं के बिना एक प्रकार पर चलाते हैं, तो विधि बारफ होगी, इसलिए सावधान रहें कि इसका उपयोग कैसे किया जाता है और यह आवश्यकता के लिए पर्याप्त होना चाहिए।

मैंने ऑर्डर की परिभाषा को छोड़ दिया है: मार्क ग्रेवेल के पद के साथ याहिया की कड़ी में एक अच्छा नमूना होने के कारण इसे स्वीकार करें।


2
यदि यह एक प्रदर्शन आवश्यकता है, तो DataAnnotations.DisplayAttribute का उपयोग करना उचित होगा , जिसमें ऑर्डर फ़ील्ड है।
जेरफ

12

MSDN के अनुसार MetadataTokenएक मॉड्यूल के अंदर अद्वितीय है - ऐसा कुछ भी नहीं कहा गया है कि यह किसी भी क्रम की गारंटी देता है।

EVEN अगर यह व्यवहार करता है जिस तरह से आप चाहते हैं कि यह कार्यान्वयन-विशिष्ट होगा और बिना सूचना के कभी भी बदल सकता है।

इस पुराने MSDN ब्लॉग प्रविष्टि को देखें ।

मैं इस तरह के कार्यान्वयन विवरण पर किसी भी निर्भरता से दूर रहने की जोरदार सिफारिश करूंगा - मार्क ग्रेवेल का यह उत्तर देखें ।

यदि आपको संकलन के समय कुछ चाहिए तो आप रोजलिन पर एक नज़र डाल सकते हैं (हालाँकि यह बहुत शुरुआती चरण में है)।


5

मेटाडेटाटोकन कार्यों द्वारा मैंने जो छँटाई का परीक्षण किया है।

यहाँ कुछ उपयोगकर्ता दावा करते हैं कि यह किसी भी तरह से अच्छा दृष्टिकोण नहीं है / विश्वसनीय नहीं है, लेकिन मैंने अभी तक उस एक का कोई सबूत नहीं देखा है - शायद आप कुछ कोड स्निपेट यहाँ पोस्ट कर सकते हैं जब दिए गए दृष्टिकोण काम नहीं करते हैं?

बैकवर्ड संगतता के बारे में - जब आप अब अपने .net 4 / .net 4.5 पर काम कर रहे हैं - Microsoft .net 5 या इससे अधिक बना रहा है, तो आप बहुत मान सकते हैं कि यह छँटाई विधि भविष्य में नहीं तोड़ी जाएगी।

निश्चित रूप से 2017 तक जब आप .net9 में अपग्रेड होंगे, तो आप संगतता ब्रेक मारेंगे, लेकिन उस समय तक Microsoft लोग संभवतः "आधिकारिक सॉर्ट तंत्र" का पता लगा लेंगे। वापस जाने या चीजों को तोड़ने का कोई मतलब नहीं है।

प्रॉपर्टी ऑर्डरिंग के लिए अतिरिक्त विशेषताओं के साथ खेलने में भी समय और कार्यान्वयन लगता है - अगर मेटाडेटाटोकन सॉर्टिंग काम करता है तो परेशान क्यों करें?


1
यह 2019 है और यह भी नहीं है .net-4.9 अभी तक जारी नहीं किया गया है :-p
binki

हर बार .net एक प्रमुख संस्करण जारी करता है जो उनके लिए मेटाडेटाटोकन या रिटर्न से ऑर्डर करने जैसी चीजों में बदलाव करने का एक शानदार अवसर है GetProperties()। आप जिस आदेश पर भरोसा कर सकते हैं, उसके लिए आपका तर्क बिल्कुल वैसा ही है जैसा कि आप भविष्य के संस्करणों पर भरोसा नहीं कर सकते हैं। यह व्यवहार नहीं बदल रहा है: हर नया रिलीज कार्यान्वयन विवरण बदलने के लिए स्वतंत्र है। अब, .net वास्तव में इस दर्शन का अनुसरण करता है कि "बग्स फीचर्स" हैं, इसलिए वे वास्तव में कभी भी ऑर्डरिंग को नहीं बदलेंगे GetProperties()। यह सिर्फ इतना है कि एपीआई का कहना है कि उन्हें अनुमति दी गई है।
बिंकी

1

आप कस्टम विशेषता के बजाय System.Component.DataAnnotations में DisplayAttribute का उपयोग कर सकते हैं। आपकी आवश्यकता वैसे भी प्रदर्शन के साथ कुछ करने की है।


0

यदि आप अतिरिक्त निर्भरता से खुश हैं, तो मार्क ग्रेवेल के प्रोटोबॉफ-नेट का उपयोग प्रतिबिंब और कैशिंग आदि को लागू करने के सर्वोत्तम तरीके के बारे में चिंता किए बिना किया जा सकता है। बस अपने खेतों का उपयोग करके सजाने [ProtoMember]और फिर उपयोग करके संख्यात्मक क्रम में खेतों तक पहुंचें।

MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)];

metaData.GetFields();

0

मैंने इसे इस तरह किया:

 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_; } }

}

यह स्वीकार किए गए उत्तर से अलग या बेहतर कैसे है?
इयान केम्प

0

उपरोक्त स्वीकृत समाधान पर निर्माण, सटीक सूचकांक प्राप्त करने के लिए आप कुछ इस तरह का उपयोग कर सकते हैं

दिया हुआ

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; }   
}

एक्सटेंशन

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);
   }
}

प्रयोग

var myClass = new MyClass();
var index = myClass.GetOrder(c => c.String2);

ध्यान दें , कोई त्रुटि जाँच या दोष सहिष्णुता नहीं है, आप स्वाद के लिए काली मिर्च और नमक जोड़ सकते हैं


0

System.ComponentModel.DataAnnotations.DisplayAttribute Orderसंपत्ति का उपयोग करने के लिए एक और संभावना है । चूंकि यह अंतर्निहित है, इसलिए एक नया विशिष्ट गुण बनाने की आवश्यकता नहीं है।

फिर इस तरह के आदेश दिए गए गुणों का चयन करें

const int defaultOrder = 10000;
var properties = type.GetProperties().OrderBy(p => p.FirstAttribute<DisplayAttribute>()?.GetOrder() ?? defaultOrder).ToArray();

और वर्ग को इस तरह प्रस्तुत किया जा सकता है

public class Toto {
    [Display(Name = "Identifier", Order = 2)
    public int Id { get; set; }

    [Display(Name = "Description", Order = 1)
    public string Label {get; set; }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.