2012-06-24 14 views
20

में पैरामीटर के रूप में कार्य करने के लिए खुद को पास करने के लिए संपत्ति को पास करें, मैं एक समारोह में संपत्ति को पास करने के लिए एक विधि की तलाश में हूं। संपत्ति का मूल्य नहीं है। फ़ंक्शन पहले से नहीं जानता है कि किस प्रकार सॉर्टिंग के लिए संपत्ति का उपयोग किया जाएगा। इस उदाहरण में सबसे आसान तरीका यह है: विभिन्न पैरामीटर प्रकारों के साथ 4 ओवरराइट बनाना। अन्य तरीके फ़ंक्शन के अंदर typeof() का उपयोग कर रहा है। कक्षा 1 में सैकड़ों गुण होने पर इन दोनों तरीकों को अस्वीकार्य माना जाता है। अब तक मुझे निम्नलिखित विधि मिली:सी #

class Class1 
{ 
    string vehName; 
    int maxSpeed; 
    int fuelCapacity; 
    bool isFlying; 
} 

class Processor 
{ 
    List<Class1> vehicles = null; 
    Processor(List<Class1> input) 
    { 
     vehicles = input; 
    } 

    List<Class1> sortBy(List<Class1> toSort, string propName) 
    { 
     if (toSort != null && toSort.Count > 0) 
     { 
      return toSort.OrderBy(x => typeof(Class1).GetProperty(propName).GetValue(x, null)).ToList(); 
     } 
     else return null; 
    } 
} 

class OuterUser 
{ 
    List<Class1> vehicles = new List<Class1>(); 
    // ... fill the list 
    Processor pr = new Processor(vehicles); 
    List<Class1> sorted = pr.sortBy("maxSpeed"); 
} 

मुझे प्रोसेसिंग फ़ंक्शन में स्ट्रिंग पास करते समय "मानव त्रुटि" के जोखिम के कारण इस विधि को पसंद नहीं है। जब कोड के दूसरे भाग द्वारा स्ट्रिंग उत्पन्न होती है तो यह और भी बदसूरत हो जा रहा है। कृपया, आगे प्रसंस्करण के लिए कार्य करने के लिए कक्षा 1 संपत्ति के उत्तीर्ण करने के लिए और अधिक शानदार तरीका सुझाएं। उपयोग आईएमएचओ के लिए सबसे अच्छा विकल्प होगा (या ऐसा कुछ):

vehicles = sortBy(vehicles, Class1.maxSpeed); 
+2

कौन सा नेट संस्करण? – alf

+0

पहली जगह में चुने गए सॉर्टिंग के लिए प्रॉपर्टी का उपयोग कैसे किया जा सकता है? – arootbeer

+0

संस्करण .NET 4.0 –

उत्तर

39

आप विधि के लिए एक संपत्ति एक्सेसर पारित कर सकते हैं।

List<Class1> SortBy(List<Class1> toSort, Func<Class1, IComparable> getProp) 
{ 
    if (toSort != null && toSort.Count > 0) { 
     return toSort 
      .OrderBy(x => getProp(x)) 
      .ToList(); 
    } 
    return null; 
} 

आप इस तरह यह कहेंगे:

var result = SortBy(toSort, x => x.maxSpeed); 

लेकिन आप एक कदम आगे जाने के लिए और अपने खुद के विस्तार विधि लिख सकते हैं।

public static class CollectionExtensions 
{ 
    public static List<TSource> OrderByAsListOrNull<TSource, TKey>(
     this ICollection<TSource> collection, Func<TSource,TKey> keySelector) 

     if (collection != null && collection.Count > 0) { 
      return collection 
       .OrderBy(x => keySelector(x)) 
       .ToList(); 
     } 
     return null; 
    } 
} 

अब आप इस

List<Class1> sorted = toSort.OrderByAsListOrNull(x => x.maxSpeed); 

लेकिन यह भी

Person[] people = ...; 
List<Person> sortedPeople = people.OrderByAsListOrNull(p => p.LastName); 

ध्यान दें कि मैं ICollection<T> के रूप में पहले पैरामीटर घोषित क्योंकि यह पूरा करना चाहिए दो शर्तों की तरह क्रमबद्ध कर सकते हैं:

  1. इसमेंहोना चाहिएसंपत्ति
  2. LINQ विधि OrderBy लागू करने में सक्षम होने के लिए यह IEnumerable<T> होना चाहिए।

List<Class1> एक ICollection<T> लेकिन यह भी एक सरणी Person[] के रूप में कई अन्य संग्रह है।

+0

क्या यह विधि LINQ के ऑर्डरबी से अलग कुछ भी करती है? –

+0

ओलिवियर, बहुत बहुत धन्यवाद! या Merci beaucoup :) –

+0

@Chris Gessler: हाँ, अगर स्रोत संग्रह 'शून्य 'है या इसकी' गणना'' 0' है तो 'null' वापस आती है, अन्यथा 'सूची ' वापस आती है। यदि स्रोत 'शून्य' है तो LINQ का 'ऑर्डरबी' अपवाद फेंकता है और अन्यथा 'IENumerable ' देता है। मुझे नहीं पता कि यह विधि दूसरों के लिए वास्तव में उपयोगी है, लेकिन ओपी को स्पष्ट रूप से इसकी आवश्यकता है। –

3

आप इसके लिए लिंक का उपयोग क्यों नहीं करते? जैसा:

vehicles.OrderBy(v => v.maxSpeed).ToList(); 
+0

अन्य कक्षाएं 'प्रोसेसर' का उपयोग करेंगी। उन्हें 'वाहन' नहीं दिखना चाहिए। इसके अलावा 'sortBy' में अधिक तर्क (आरोही/अवरोही/कुछ फ़िल्टरिंग) होगा ... –

+0

तब मुझे लगता है कि आपको उपरोक्त @olivier से जवाब देखना चाहिए, अपनी खुद की विस्तार विधि लिखने के बारे में (फ़िल्टरिंग आदि को जोड़ने के लिए) – joakimbeng

17

आप संपत्ति के बारे में जानकारी पारित करने के लिए एक लैम्ब्डा अभिव्यक्ति का उपयोग कर सकते हैं:

void DoSomething<T>(Expression<Func<T>> property) 
{ 
    var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo; 
    if (propertyInfo == null) 
    { 
     throw new ArgumentException("The lambda expression 'property' should point to a valid Property"); 
    } 
} 

उपयोग:

DoSomething(() => this.MyProperty); 
+0

यदि आप किसी संपत्ति के बारे में अधिक जानकारी प्राप्त करने की आवश्यकता है, उदाहरण के लिए संपत्ति का नाम ('INotifyPropertyChanged' को कार्यान्वित करते समय उपयोगी) के लिए आप इस दृष्टिकोण का उपयोग कर सकते हैं। हालांकि, इस स्थिति में यह बहुत अधिक है, क्योंकि यह संपत्ति मूल्य वापस करने के लिए पूरी तरह से पर्याप्त है। –

0

बस ऊपर जवाब से जोड़ने के लिए। आप ऑर्डर दिशा के लिए एक साधारण ध्वज भी कर सकते हैं।

public class Processor 
{ 
    public List<SortableItem> SortableItems { get; set; } 

    public Processor() 
    { 
     SortableItems = new List<SortableItem>(); 
     SortableItems.Add(new SortableItem { PropA = "b" }); 
     SortableItems.Add(new SortableItem { PropA = "a" }); 
     SortableItems.Add(new SortableItem { PropA = "c" }); 
    } 

    public void SortItems(Func<SortableItem, IComparable> keySelector, bool isAscending) 
    { 
     if(isAscending) 
      SortableItems = SortableItems.OrderBy(keySelector).ToList(); 
     else 
      SortableItems = SortableItems.OrderByDescending(keySelector).ToList(); 
    } 
} 
3

मुझे @ मैथियसजी के उत्तर से जो गुम मिला, वह यह है कि संपत्ति मूल्य को केवल उसका नाम कैसे प्राप्त करें।

public static string Meth<T>(Expression<Func<T>> expression) 
{ 
    var name = ((MemberExpression)expression.Body).Member.Name; 
    var value = expression.Compile()(); 
    return string.Format("{0} - {1}", name, value); 
} 

उपयोग:

Meth(() => YourObject.Property); 
2

यहाँ पर महान समाधान ...

Passing properties by reference in C#

void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr) 
{ 
    if (!string.IsNullOrEmpty(input)) 
    { 
     var expr = (MemberExpression) outExpr.Body; 
     var prop = (PropertyInfo) expr.Member; 
     prop.SetValue(target, input, null); 
    } 
} 

void Main() 
{ 
    var person = new Person(); 
    GetString("test", person, x => x.Name); 
    Debug.Assert(person.Name == "test"); 
}