2011-07-26 9 views
8

एफ # "के साथ", उदाहरण के लिए एक सुविधाजनक सुविधा है:सी #: एफ # में एक एक्सटेंशन विधि को "साथ" के रूप में परिभाषित करने के लिए कैसे करें?

type Product = { Name:string; Price:int };; 
let p = { Name="Test"; Price=42; };; 
let p2 = { p with Name="Test2" };; 

एफ # बनाई कीवर्ड "के साथ" के रूप में रिकॉर्ड प्रकारों डिफ़ॉल्ट अपरिवर्तनीय द्वारा कर रहे हैं।

अब, क्या सी # में समान एक्सटेंशन को परिभाषित करना संभव है? मुझे यकीन है कि कैसे एक स्ट्रिंग

Name="Test2" 
एक प्रतिनिधि या अभिव्यक्ति को

कन्वर्ट करने के लिए नहीं कर रहा हूँ यह थोड़ा मुश्किल है, सी # में के रूप में लगता है?

+0

मैं इन सवालों के और अधिक देखने के रूप में कार्यात्मक भाषाओं मुख्यधारा बन उम्मीद है। एक सामान्य बचना "मैं [मेरी पसंदीदा भाषा] में [कुछ कार्यात्मक विशेषता] कैसे कर सकता हूं?" – Daniel

उत्तर

4
public static T With<T, U>(this T obj, Expression<Func<T, U>> property, U value) 
    where T : ICloneable { 
    if (obj == null) 
     throw new ArgumentNullException("obj"); 
    if (property == null) 
     throw new ArgumentNullException("property"); 
    var memExpr = property.Body as MemberExpression; 
    if (memExpr == null || !(memExpr.Member is PropertyInfo)) 
     throw new ArgumentException("Must refer to a property", "property"); 
    var copy = (T)obj.Clone(); 
    var propInfo = (PropertyInfo)memExpr.Member; 
    propInfo.SetValue(copy, value, null); 
    return copy; 
} 

public class Foo : ICloneable { 
    public int Id { get; set; } 
    public string Bar { get; set; } 
    object ICloneable.Clone() { 
     return new Foo { Id = this.Id, Bar = this.Bar }; 
    } 
} 

public static void Test() { 
    var foo = new Foo { Id = 1, Bar = "blah" }; 
    var newFoo = foo.With(x => x.Bar, "boo-ya"); 
    Console.WriteLine(newFoo.Bar); //boo-ya 
} 

या, एक प्रति निर्माता का उपयोग कर:

public class Foo { 
    public Foo(Foo other) { 
     this.Id = other.Id; 
     this.Bar = other.Bar; 
    } 
    public Foo() { } 
    public int Id { get; set; } 
    public string Bar { get; set; } 
} 

public static void Test() { 
    var foo = new Foo { Id = 1, Bar = "blah" }; 
    var newFoo = new Foo(foo) { Bar = "boo-ya" }; 
    Console.WriteLine(newFoo.Bar); 
} 

और जॉर्ज उत्कृष्ट सुझाव पर एक मामूली बदलाव, जो एक से अधिक कार्य के लिए अनुमति देता है:

public static T With<T>(this T obj, params Action<T>[] assignments) 
    where T : ICloneable { 
    if (obj == null) 
     throw new ArgumentNullException("obj"); 
    if (assignments == null) 
     throw new ArgumentNullException("assignments"); 
    var copy = (T)obj.Clone(); 
    foreach (var a in assignments) { 
     a(copy); 
    } 
    return copy; 
} 

public static void Test() { 
    var foo = new Foo { Id = 1, Bar = "blah" }; 
    var newFoo = foo.With(x => x.Id = 2, x => x.Bar = "boo-ya"); 
    Console.WriteLine(newFoo.Bar); 
} 

मैं शायद दूसरे के बाद से उपयोग करूंगा (1) किसी भी सामान्य प्रयोजन समाधान को अनावश्यक रूप से धीमा और गड़बड़ाना होगा; (2) यह आपके इच्छित चीज़ों के निकटतम वाक्यविन्यास है (और वाक्यविन्यास जो आप उम्मीद करते हैं); (3) एफ # कॉपी-एंड-अपडेट एक्सप्रेशन समान रूप से कार्यान्वित किए जाते हैं।

+0

यह करना संभव है: var newFoo = foowith (बार = "बू-या"); ? – athos

+0

@athos: हाँ, मैंने एक और जोड़ा (बेहतर, हालांकि सामान्य उद्देश्य नहीं) विधि। – Daniel

+0

@athos: मेरी इच्छा है कि एक कॉपी कन्स्ट्रक्टर बाधा थी (शायद 'जहां टी: नया (टी) ') ताकि हम इसके सामान्यीकृत समाधानों पर उछाल सकें। शायद अपरिवर्तनीयता सी # में बेहतर समर्थन प्राप्त करेगी, हम उन सुविधाओं को देखेंगे जो इसे आसान बनाते हैं। – Daniel

2

हो सकता है कि कुछ इस तरह:

void Main() 
{ 
    var NewProduct = ExistingProduct.With(P => P.Name = "Test2"); 
} 

// Define other methods and classes here 

public static class Extensions 
{ 
    public T With<T>(this T Instance, Action<T> Act) where T : ICloneable 
    { 
     var Result = Instance.Clone(); 
     Act(Result); 

     return Result; 
    } 
} 
+0

ऐसा कुछ करना संभव है जैसे: var newProduct = ExistingProduct.With (नाम = "test2"); ? – athos

+0

यह संभव नहीं है, आप स्ट्रिंग का उपयोग कर सकते हैं। स्प्लिट और प्रतिबिंब, कुछ ऐसा करें: var newProduct = ExistingProduct.With ("name = 'Test2'"); लेकिन फिर आप कोई संकलन समय जांच नहीं पाते हैं और आप एक महत्वपूर्ण प्रदर्शन हिट लेते हैं इसलिए मैं इसकी अनुशंसा नहीं करता। एक लैम्ब्डा अभिव्यक्ति उतनी ही अच्छी है जितनी आपको मिलने की संभावना है। – ForbesLindesay

+0

बहुत पठनीय। +1 – vlad

0

लैम्ब्डा फ़ंक्शन के विकल्प के रूप में, आप डिफ़ॉल्ट मानों के साथ पैरामीटर का उपयोग कर सकते हैं। केवल मामूली मुद्दा आप कुछ डिफ़ॉल्ट मान का मतलब है कि (संदर्भ प्रकार के लिए) इस पैरामीटर में परिवर्तन नहीं करते लेने के लिए है कि है, लेकिन null एक सुरक्षित विकल्प होना चाहिए:

class Product { 
    public string Name { get; private set; } 
    public int Price { get; private set; } 
    public Product(string name, int price) { 
    Name = name; Price = price; 
    } 

    // Creates a new product using the current values and changing 
    // the values of the specified arguments to a new value 
    public Product With(string name = null, int? price = null) { 
    return new Product(name ?? Name, price ?? Price); 
    } 
} 

// Then you can write: 
var prod2 = prod1.With(name = "New product"); 

आप विधि अपने आप को परिभाषित करने के लिए है , लेकिन यह हमेशा मामला है (जब तक कि आप प्रतिबिंब का उपयोग नहीं कर रहे हैं, जो कम कुशल है)। मुझे लगता है कि वाक्यविन्यास भी काफी अच्छा है। यदि आप इसे F # में जितना अच्छा बनाना चाहते हैं, तो आपको F # :-)

+1

मैं सहमत हूं, बस F # का उपयोग करें! – Daniel

+0

हाहा तोमास, आप जानते हैं कि, मैं अभी आपकी "एफ # भाषा समीक्षा" पढ़ रहा हूं! आपको यहां देखने के लिए अच्छा लगा :) – athos

+0

हालांकि यह "साथ" उत्पाद प्रकार पर निर्भर करता है, डिफ़ॉल्ट मानों वाले पैरामीटर का उपयोग करना एक स्मार्ट मुश्किल और दिलचस्प है! :) – athos

0

किसी एक्सटेंशन विधि के C# में कम करने की कोई मूल क्षमता नहीं है, लेकिन किस कीमत पर? एक और संदर्भ प्रकार और एक कारणों तत्काल भ्रम पर किसी भी सुझाव है कि आधारित है कि कितने वस्तुओं हम साथ काम कर रहे है के रूप में ("के साथ") कर रहे हैं। क्या केवल एक है? बी की एक प्रति? बी?

सी # एफ # नहीं है।

के रूप में एरिक Lippert द्वारा उत्तर दिया कृपया मेरा एक पिछले तो सवाल यह देखें:

"स्पष्ट कोड लिखने के लिए अंगूठे का मेरा नियम के बीच है: बयान में सभी दुष्प्रभाव डाल; गैर बयान भाव कोई साइड होना चाहिए प्रभाव। "

More fluent C#/.NET

+0

हाय एंडलेर, एरिक के अंक हमेशा दूसरे विचार के योग्य होते हैं। बस .. अगर हम वर्तमान टीडीडी ढांचे की जांच करते हैं जैसे कि न्यूटिट, आपके प्रश्न और मेरा जैसी रेखाएं हैं ... एक तरह से मुझे लगता है कि "प्राकृतिक अंग्रेजी के रूप में कोड लिखना" में कुछ गुण हैं ... यदि आपको लगता है अन्यथा, ठीक है, हम अभी भी मज़े के लिए कुछ सवाल पूछ सकते हैं! : पी – athos

+0

एथोस, व्यक्तिगत स्तर पर मैं इस बारे में दृढ़ता से महसूस नहीं करता हूं। एक पेशेवर स्तर पर, मुझे एरिक के जवाब पसंद आया क्योंकि इससे मुझे अपने सहकर्मियों के साथ मजबूत तर्क देने की इजाजत मिली। इस पर चारों ओर जाने की जरूरत नहीं है। सौभाग्य! – andleer

+0

@andleer - मुझे लगता है कि यह पूछने के लिए एक उचित सवाल है कि सी # में एफ # ''' के साथ कुछ निर्माण कैसे प्राप्त करें। इसका उपयोग केवल तभी समझ में आता है यदि आप _immutable_ प्रकारों के साथ काम कर रहे हैं, लेकिन यह सी # में पूरी तरह से उपयोगी दृष्टिकोण है (बाद में, सभी मान प्रकार अपरिवर्तनीय होना चाहिए)। यदि प्रकार अपरिवर्तनीय है तो आपकी सभी चिंताओं को कोई समस्या नहीं है। बेशक, जब म्यूटेबल प्रकार का उपयोग करते हैं, तो चीजें अधिक कठिन हो जाती हैं, लेकिन जब मैं प्रश्न में "एफ # रिकॉर्ड" देखता हूं तो यह नहीं है :-)। –

संबंधित मुद्दे

 संबंधित मुद्दे