C# Language
एक्सटेंशन के तरीके
खोज…
वाक्य - विन्यास
- सार्वजनिक स्थैतिक ReturnType MyExtensionMethod (यह TargetType लक्ष्य)
- सार्वजनिक स्थैतिक रिटर्न टाइप MyExtensionMethod (यह TargetType लक्ष्य, TArg1 arg1, ...)
पैरामीटर
पैरामीटर | विवरण |
---|---|
इस | एक्सटेंशन पद्धति का पहला पैरामीटर हमेशा this कीवर्ड से पहले होना चाहिए, इसके बाद पहचानकर्ता जिसके साथ आप उस वस्तु के "वर्तमान" उदाहरण को देखें जिसका आप विस्तार कर रहे हैं |
टिप्पणियों
विस्तार विधियाँ वाक्यगत शर्करा होती हैं जो स्थैतिक विधियों को ऑब्जेक्ट इंस्टेंसेस पर लागू करने की अनुमति देती हैं जैसे कि वे स्वयं प्रकार के सदस्य थे।
विस्तार विधियों के लिए एक स्पष्ट लक्ष्य वस्तु की आवश्यकता होती है। आपको विस्तारित प्रकार के भीतर से विधि तक पहुंचने के लिए this
कीवर्ड का उपयोग करना होगा।
एक्सटेंशन विधियों को स्थिर घोषित किया जाना चाहिए, और एक स्थिर वर्ग में रहना चाहिए।
कौन से नामस्थान?
आपके विस्तार विधि वर्ग के लिए नाम स्थान का चुनाव दृश्यता और खोज के बीच का व्यापार है।
सबसे अधिक उल्लिखित विकल्प आपके एक्सटेंशन विधियों के लिए एक कस्टम नेमस्पेस है। हालाँकि इसमें एक संचार प्रयास शामिल होगा ताकि आपके कोड के उपयोगकर्ताओं को पता चले कि विस्तार विधियाँ मौजूद हैं, और उन्हें कहाँ खोजना है।
एक विकल्प एक नाम स्थान चुनने का है, जिससे डेवलपर्स Intellisense के माध्यम से आपके विस्तार के तरीकों की खोज करेंगे। इसलिए यदि आप Foo
कक्षा का विस्तार करना चाहते हैं, तो विस्तार विधियों को Foo
के समान नामस्थान में रखना तर्कसंगत है।
यह महसूस करना महत्वपूर्ण है कि "किसी और के" नाम स्थान का उपयोग करने से आपको कुछ भी नहीं रोकता है : इस प्रकार यदि आप IEnumerable
का विस्तार करना चाहते हैं, तो आप System.Linq
नाम स्थान में अपनी एक्सटेंशन विधि जोड़ सकते हैं।
यह हमेशा एक अच्छा विचार नहीं है। उदाहरण के लिए, एक विशिष्ट मामले में, आप एक सामान्य प्रकार ( bool IsApproxEqualTo(this double value, double other)
उदाहरण के लिए) का विस्तार करना चाह सकते हैं, लेकिन पूरे System
'प्रदूषण' नहीं है। इस मामले में स्थानीय, विशिष्ट, नाम स्थान चुनना बेहतर होता है।
अंत में, विस्तार विधियों को बिना किसी नाम के स्थान पर रखना संभव है!
एक अच्छा संदर्भ प्रश्न: आप अपनी एक्सटेंशन विधियों के नामस्थान कैसे प्रबंधित करते हैं?
प्रयोज्यता
एक्सटेंशन विधियों का निर्माण करते समय ध्यान रखा जाना चाहिए ताकि यह सुनिश्चित हो सके कि वे सभी संभावित आदानों के लिए उपयुक्त हैं और केवल विशिष्ट स्थितियों के लिए प्रासंगिक नहीं हैं। उदाहरण के लिए, सिस्टम क्लास जैसे string
को विस्तारित करना संभव है, जो आपके नए कोड को किसी भी स्ट्रिंग के लिए उपलब्ध कराता है। यदि आपके कोड को डोमेन विशिष्ट स्ट्रिंग प्रारूप पर डोमेन विशिष्ट तर्क करने की आवश्यकता है, तो एक विस्तार विधि उचित नहीं होगी क्योंकि इसकी उपस्थिति सिस्टम में अन्य स्ट्रिंग के साथ काम करने वाले कॉलर्स को भ्रमित करेगी।
निम्नलिखित सूची में विस्तार विधियों की बुनियादी विशेषताएं और गुण हैं
- यह एक स्थिर विधि होनी चाहिए।
- यह एक स्थिर वर्ग में स्थित होना चाहिए।
- यह "इस" कीवर्ड का उपयोग करता है। .NET में एक प्रकार के साथ पहला पैरामीटर है और इस पद्धति को क्लाइंट साइड पर दिए गए प्रकार के उदाहरण से बुलाया जाएगा।
- यह भी वी.एस. intellisense द्वारा दिखाया गया है। जब हम डॉट दबाते हैं
.
एक प्रकार के उदाहरण के बाद, फिर यह वी.एस. - एक एक्सटेंशन विधि समान नामस्थान में होनी चाहिए क्योंकि इसका उपयोग किया जाता है या आपको एक उपयोग कथन द्वारा कक्षा के नाम स्थान को आयात करने की आवश्यकता होती है।
- आप उस वर्ग के लिए कोई भी नाम दे सकते हैं, जिसमें विस्तार विधि है, लेकिन कक्षा स्थिर होनी चाहिए।
- यदि आप नए तरीकों को एक प्रकार से जोड़ना चाहते हैं और आपके पास इसके लिए स्रोत कोड नहीं है, तो समाधान उस प्रकार के विस्तार विधियों का उपयोग और कार्यान्वयन करना है।
- यदि आप एक्सटेंशन विधियाँ बनाते हैं जिसमें आपके हस्ताक्षर प्रकार समान हैं जो आप बढ़ा रहे हैं, तो विस्तार विधियों को कभी नहीं कहा जाएगा।
विस्तार के तरीके - अवलोकन
एक्सटेंशन विधियों को C # 3.0 में पेश किया गया था। विस्तार विधियाँ एक नए व्युत्पन्न प्रकार को बनाए बिना मौजूदा प्रकारों में व्यवहार को बढ़ाती हैं और जोड़ती हैं, पुन: जमा करती हैं, या अन्यथा मूल प्रकार को संशोधित करती हैं। जब आप उस प्रकार के स्रोत को संशोधित नहीं कर सकते, जिसे आप बढ़ाने के लिए देख रहे हैं, तो वे विशेष रूप से सहायक होते हैं। एक्सटेंशन के तरीके सिस्टम प्रकार के लिए बनाए जा सकते हैं, तीसरे पक्ष द्वारा परिभाषित प्रकार, और प्रकार जो आपने खुद को परिभाषित किया है। विस्तार विधि को लागू किया जा सकता है क्योंकि यह मूल प्रकार का सदस्य तरीका था। यह एक धाराप्रवाह इंटरफ़ेस को लागू करने के लिए उपयोग की जाने वाली विधि जंजीर के लिए अनुमति देता है।
एक विस्तार विधि एक स्थिर विधि को एक स्थिर वर्ग में जोड़कर बनाई जाती है जो मूल प्रकार के विस्तार से अलग है। एक्सटेंशन पद्धति को धारण करने वाला स्थिर वर्ग अक्सर एक्सटेंशन विधियों को रखने के एकमात्र उद्देश्य के लिए बनाया जाता है।
एक्सटेंशन विधियाँ एक विशेष पहला पैरामीटर लेती हैं जो मूल प्रकार को बढ़ाता है। यह पहली पैरामीटर कीवर्ड से सजाया गया है this
(जिनमें से एक विशेष और विशिष्ट उपयोग का गठन this
सी # में इसे 'के उपयोग से अलग रूप में समझा जाना चाहिए this
जो वर्तमान वस्तु उदाहरण के सदस्यों की चर्चा करते हुए अनुमति देता है)।
निम्नलिखित उदाहरण में, मूल प्रकार को बढ़ाया जा रहा है, कक्षा string
। String
को एक विधि Shorten()
द्वारा बढ़ाया गया है, जो छोटा करने की अतिरिक्त कार्यक्षमता प्रदान करता है। एक्सटेंशन पद्धति को रखने के लिए स्टैटिक क्लास StringExtensions
बनाया गया है। विस्तार विधि Shorten()
पता चलता है कि यह विशेष रूप से चिह्नित पहले पैरामीटर के माध्यम से string
विस्तार है। कि दिखाने के लिए Shorten()
विधि फैली string
, पहले पैरामीटर के साथ चिह्नित है this
। इसलिए, पहले पैरामीटर का पूर्ण हस्ताक्षर this string text
, जहां string
मूल प्रकार बढ़ाया जा रहा है और text
चुना हुआ पैरामीटर नाम है।
static class StringExtensions
{
public static string Shorten(this string text, int length)
{
return text.Substring(0, length);
}
}
class Program
{
static void Main()
{
// This calls method String.ToUpper()
var myString = "Hello World!".ToUpper();
// This calls the extension method StringExtensions.Shorten()
var newString = myString.Shorten(5);
// It is worth noting that the above call is purely syntactic sugar
// and the assignment below is functionally equivalent
var newString2 = StringExtensions.Shorten(myString, 5);
}
}
एक्सटेंशन विधि के पहले तर्क के रूप में पारित वस्तु (जो this
कीवर्ड के साथ है) वह उदाहरण है जिस पर एक्सटेंशन विधि कहा जाता है।
उदाहरण के लिए, जब यह कोड निष्पादित होता है:
"some string".Shorten(5);
तर्कों के मूल्य इस प्रकार हैं:
text: "some string"
length: 5
ध्यान दें कि एक्सटेंशन विधियाँ केवल तब ही उपयोग करने योग्य होती हैं, यदि वे समान परिभाषा में उनकी परिभाषा के समान नामस्थान में हों, यदि एक्सटेंशन विधि का उपयोग करके कोड द्वारा स्पष्ट रूप से आयात किया जाता है, या यदि एक्सटेंशन वर्ग नेमस्पेस-कम है। .NET फ्रेमवर्क दिशानिर्देश अपने नामस्थान में विस्तार कक्षाएं लगाने की सलाह देते हैं। हालाँकि, इससे डिस्कवरी संबंधी समस्याएं हो सकती हैं।
इसके परिणामस्वरूप विस्तार विधियों और पुस्तकालयों के बीच कोई संघर्ष नहीं होता है, जब तक कि नाम स्थान जो संघर्ष नहीं हो सकता है, स्पष्ट रूप से खींच लिया जाता है। उदाहरण के लिए LINQ एक्सटेंशन :
using System.Linq; // Allows use of extension methods from the System.Linq namespace
class Program
{
static void Main()
{
var ints = new int[] {1, 2, 3, 4};
// Call Where() extension method from the System.Linq namespace
var even = ints.Where(x => x % 2 == 0);
}
}
C # 6.0 के बाद से, क्लास के विस्तार निर्देशों वाले एक using static
निर्देश using static
करना भी संभव है। उदाहरण के लिए, using static System.Linq.Enumerable;
। यह एक ही नामस्थान से अन्य प्रकारों को दायरे में लाए बिना उस विशेष वर्ग से विस्तार विधियाँ उपलब्ध कराता है।
जब समान हस्ताक्षर वाली एक क्लास विधि उपलब्ध होती है, तो कंपाइलर इसे एक्सटेंशन विधि कॉल पर प्राथमिकता देता है। उदाहरण के लिए:
class Test
{
public void Hello()
{
Console.WriteLine("From Test");
}
}
static class TestExtensions
{
public static void Hello(this Test test)
{
Console.WriteLine("From extension method");
}
}
class Program
{
static void Main()
{
Test t = new Test();
t.Hello(); // Prints "From Test"
}
}
ध्यान दें कि यदि एक ही हस्ताक्षर के साथ दो एक्सटेंशन फ़ंक्शन हैं, और उनमें से एक समान नामस्थान में है, तो उस एक को प्राथमिकता दी जाएगी। दूसरी ओर, यदि दोनों का using
पहुँचा जाता है, तो संदेश के साथ एक संकलन समय त्रुटि हो जाएगी:
कॉल निम्न विधियों या गुणों के बीच अस्पष्ट है
ध्यान दें कि originalTypeInstance.ExtensionMethod()
माध्यम से एक्सटेंशन विधि को कॉल करने की सिंटैक्टिक सुविधा एक वैकल्पिक सुविधा है। विधि को पारंपरिक तरीके से भी कहा जा सकता है, ताकि विशेष पहले पैरामीटर का उपयोग विधि के पैरामीटर के रूप में किया जाए।
यानी, निम्नलिखित दोनों कार्य:
//Calling as though method belongs to string--it seamlessly extends string
String s = "Hello World";
s.Shorten(5);
//Calling as a traditional static method with two parameters
StringExtensions.Shorten(s, 5);
विस्तार विधि का स्पष्ट रूप से उपयोग करना
एक्सटेंशन विधियों का उपयोग साधारण स्थिर वर्ग विधियों की तरह भी किया जा सकता है। एक्सटेंशन पद्धति को कॉल करने का यह तरीका अधिक क्रियात्मक है, लेकिन कुछ मामलों में आवश्यक है।
static class StringExtensions
{
public static string Shorten(this string text, int length)
{
return text.Substring(0, length);
}
}
उपयोग:
var newString = StringExtensions.Shorten("Hello World", 5);
जब विस्तार विधियों को स्थिर तरीकों के रूप में कॉल करना है
अभी भी ऐसे परिदृश्य हैं जहां आपको स्थैतिक विधि के रूप में विस्तार विधि का उपयोग करने की आवश्यकता होगी:
- एक सदस्य विधि के साथ संघर्ष का समाधान। यह तब हो सकता है जब एक पुस्तकालय का नया संस्करण उसी हस्ताक्षर के साथ एक नई सदस्य विधि का परिचय देता है। इस मामले में, सदस्य विधि संकलक द्वारा पसंद की जाएगी।
- एक ही हस्ताक्षर के साथ एक और विस्तार विधि के साथ संघर्ष का समाधान। यह तब हो सकता है जब दो पुस्तकालयों में समान विस्तार विधियाँ शामिल हों और दोनों वर्गों के विस्तार विधियों के नामस्थान एक ही फ़ाइल में उपयोग किए जाएँ।
- प्रतिनिधि समूह में एक विधि समूह के रूप में विस्तार विधि पास करना।
-
Reflection
माध्यम से अपने स्वयं के बंधन करना। - Visual Studio में तत्काल विंडो में एक्सटेंशन विधि का उपयोग करना।
स्थिर का उपयोग करना
यदि एक using static
निर्देश का उपयोग किसी स्थिर वर्ग के स्थिर सदस्यों को वैश्विक दायरे में लाने के लिए किया जाता है, तो विस्तार विधियों को छोड़ दिया जाता है। उदाहरण:
using static OurNamespace.StringExtensions; // refers to class in previous example
// OK: extension method syntax still works.
"Hello World".Shorten(5);
// OK: static method syntax still works.
OurNamespace.StringExtensions.Shorten("Hello World", 5);
// Compile time error: extension methods can't be called as static without specifying class.
Shorten("Hello World", 5);
यदि आप this
संशोधक को Shorten
विधि के पहले तर्क से हटाते हैं, तो अंतिम पंक्ति संकलित होगी।
अशक्त जाँच
एक्सटेंशन मेथड्स स्टैटिक मेथड्स होते हैं जो इंस्टेंस मेथड्स की तरह व्यवहार करते हैं। हालांकि, एक null
संदर्भ पर एक इंस्टेंस विधि को कॉल करते समय क्या होता है, इसके विपरीत, जब एक्सटेंशन एक्सटेंशन को एक null
संदर्भ के साथ कहा जाता है, तो यह NullReferenceException
को नहीं फेंकता है। यह कुछ परिदृश्यों में काफी उपयोगी हो सकता है।
उदाहरण के लिए, निम्नलिखित स्थिर वर्ग पर विचार करें:
public static class StringExtensions
{
public static string EmptyIfNull(this string text)
{
return text ?? String.Empty;
}
public static string NullIfEmpty(this string text)
{
return String.Empty == text ? null : text;
}
}
string nullString = null;
string emptyString = nullString.EmptyIfNull();// will return ""
string anotherNullString = emptyString.NullIfEmpty(); // will return null
एक्सटेंशन विधियां केवल विस्तारित वर्ग के सार्वजनिक (या आंतरिक) सदस्यों को देख सकती हैं
public class SomeClass
{
public void DoStuff()
{
}
protected void DoMagic()
{
}
}
public static class SomeClassExtensions
{
public static void DoStuffWrapper(this SomeClass someInstance)
{
someInstance.DoStuff(); // ok
}
public static void DoMagicWrapper(this SomeClass someInstance)
{
someInstance.DoMagic(); // compilation error
}
}
एक्सटेंशन विधियां केवल एक वाक्यविन्यास चीनी हैं, और वास्तव में वे जिस वर्ग का विस्तार करते हैं, उसके सदस्य नहीं हैं। इसका मतलब है कि वे एनकैप्सुलेशन को नहीं तोड़ सकते हैं - उनके पास केवल public
(या जब एक ही विधानसभा, internal
) क्षेत्रों, गुणों और विधियों में लागू किया जाता है।
सामान्य विस्तार के तरीके
अन्य विधियों की तरह, विस्तार विधियाँ जेनरिक का उपयोग कर सकती हैं। उदाहरण के लिए:
static class Extensions
{
public static bool HasMoreThanThreeElements<T>(this IEnumerable<T> enumerable)
{
return enumerable.Take(4).Count() > 3;
}
}
और फोन करना इस तरह होगा:
IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};
var hasMoreThanThreeElements = numbers.HasMoreThanThreeElements();
इसी तरह कई प्रकार के तर्क के लिए:
public static TU GenericExt<T, TU>(this T obj)
{
TU ret = default(TU);
// do some stuff with obj
return ret;
}
इसे कॉल करना इस तरह होगा:
IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};
var result = numbers.GenericExt<IEnumerable<int>,String>();
आप बहु-सामान्य प्रकारों में आंशिक रूप से बाध्य प्रकारों के लिए विस्तार विधियाँ भी बना सकते हैं:
class MyType<T1, T2>
{
}
static class Extensions
{
public static void Example<T>(this MyType<int, T> test)
{
}
}
इसे कॉल करना इस तरह होगा:
MyType<int, string> t = new MyType<int, string>();
t.Example();
तुम भी साथ प्रकार की कमी निर्दिष्ट कर सकते हैं where
:
public static bool IsDefault<T>(this T obj) where T : struct, IEquatable<T>
{
return EqualityComparer<T>.Default.Equals(obj, default(T));
}
कॉलिंग कोड:
int number = 5;
var IsDefault = number.IsDefault();
विस्तार विधियाँ स्थैतिक प्रकार के आधार पर प्रेषित होती हैं
पैरामीटर से मिलान करने के लिए गतिशील (रन-टाइम प्रकार) के बजाय स्थिर (संकलन-समय) प्रकार का उपयोग किया जाता है।
public class Base
{
public virtual string GetName()
{
return "Base";
}
}
public class Derived : Base
{
public override string GetName()
{
return "Derived";
}
}
public static class Extensions
{
public static string GetNameByExtension(this Base item)
{
return "Base";
}
public static string GetNameByExtension(this Derived item)
{
return "Derived";
}
}
public static class Program
{
public static void Main()
{
Derived derived = new Derived();
Base @base = derived;
// Use the instance method "GetName"
Console.WriteLine(derived.GetName()); // Prints "Derived"
Console.WriteLine(@base.GetName()); // Prints "Derived"
// Use the static extension method "GetNameByExtension"
Console.WriteLine(derived.GetNameByExtension()); // Prints "Derived"
Console.WriteLine(@base.GetNameByExtension()); // Prints "Base"
}
}
इसके अलावा स्थैतिक प्रकार पर आधारित प्रेषण एक विस्तार विधि को dynamic
वस्तु पर कॉल करने की अनुमति नहीं देता है:
public class Person
{
public string Name { get; set; }
}
public static class ExtenionPerson
{
public static string GetPersonName(this Person person)
{
return person.Name;
}
}
dynamic person = new Person { Name = "Jon" };
var name = person.GetPersonName(); // RuntimeBinderException is thrown
एक्सटेंशन कोड डायनेमिक कोड द्वारा समर्थित नहीं हैं।
static class Program
{
static void Main()
{
dynamic dynamicObject = new ExpandoObject();
string awesomeString = "Awesome";
// Prints True
Console.WriteLine(awesomeString.IsThisAwesome());
dynamicObject.StringValue = awesomeString;
// Prints True
Console.WriteLine(StringExtensions.IsThisAwesome(dynamicObject.StringValue));
// No compile time error or warning, but on runtime throws RuntimeBinderException
Console.WriteLine(dynamicObject.StringValue.IsThisAwesome());
}
}
static class StringExtensions
{
public static bool IsThisAwesome(this string value)
{
return value.Equals("Awesome");
}
}
कारण [डायनेमिक कोड से कॉलिंग एक्सटेंशन विधियाँ] काम नहीं करती है क्योंकि नियमित रूप से, गैर-डायनेमिक कोड एक्सटेंशन मेथड्स स्टैटिक क्लास के लिए संकलित सभी वर्गों की पूरी खोज करके काम करता है जिसमें एक एक्सटेंशन विधि होती है जो मेल खाती है । खोज नेमस्पेस नामकरण पर आधारित है और प्रत्येक नामस्थान में निर्देशों
using
उपलब्ध है।इसका मतलब है कि एक डायनामिक एक्सटेंशन मेथड इनवोकेशन को सही तरीके से हल करने के लिए, किसी तरह डीएलआर को रनटाइम में जानना होगा कि सभी नेमस्पेस घोंसले और निर्देशों
using
आपके स्रोत कोड में क्या था । कॉल साइट में उस जानकारी को एन्कोडिंग के लिए हमारे पास एक तंत्र काम नहीं है। हमने इस तरह के एक तंत्र का आविष्कार करने पर विचार किया, लेकिन यह तय किया कि यह बहुत अधिक लागत वाला था और इसके लायक होने के लिए बहुत अधिक शेड्यूल जोखिम का उत्पादन किया।
विस्तार से तरीके के रूप में जोरदार टाइपर्स
विस्तार-विधियों का उपयोग शब्दकोश-जैसी वस्तुओं के लिए जोरदार टाइपर्स लिखने के लिए किया जा सकता है। उदाहरण के लिए कैश, HttpContext.Items
at cetera ...
public static class CacheExtensions
{
public static void SetUserInfo(this Cache cache, UserInfo data) =>
cache["UserInfo"] = data;
public static UserInfo GetUserInfo(this Cache cache) =>
cache["UserInfo"] as UserInfo;
}
यह दृष्टिकोण कोडबेस के साथ-साथ रीड ऑपरेशन के दौरान आवश्यक प्रकार के लिए कास्टिंग की आवश्यकता के रूप में स्ट्रिंग शाब्दिक का उपयोग करने की आवश्यकता को हटा देता है। कुल मिलाकर यह शब्दकोश के रूप में इस तरह की शिथिल टाइप की वस्तुओं के साथ बातचीत का एक अधिक सुरक्षित, दृढ़ता से टाइप किया हुआ तरीका बनाता है।
चैनिंग के लिए विस्तार विधियाँ
जब कोई एक्सटेंशन विधि उस मान को लौटाती है जिसका प्रकार this
तर्क के समान होता है, तो इसका उपयोग संगत हस्ताक्षर के साथ एक या अधिक विधि कॉल को "चेन" करने के लिए किया जा सकता है। यह सील और / या आदिम प्रकारों के लिए उपयोगी हो सकता है, और तथाकथित "धाराप्रवाह" एपीआई के निर्माण की अनुमति देता है अगर विधि नाम प्राकृतिक मानव भाषा की तरह पढ़ा जाता है।
void Main()
{
int result = 5.Increment().Decrement().Increment();
// result is now 6
}
public static class IntExtensions
{
public static int Increment(this int number) {
return ++number;
}
public static int Decrement(this int number) {
return --number;
}
}
या इस तरह
void Main()
{
int[] ints = new[] { 1, 2, 3, 4, 5, 6};
int[] a = ints.WhereEven();
//a is { 2, 4, 6 };
int[] b = ints.WhereEven().WhereGreaterThan(2);
//b is { 4, 6 };
}
public static class IntArrayExtensions
{
public static int[] WhereEven(this int[] array)
{
//Enumerable.* extension methods use a fluent approach
return array.Where(i => (i%2) == 0).ToArray();
}
public static int[] WhereGreaterThan(this int[] array, int value)
{
return array.Where(i => i > value).ToArray();
}
}
इंटरफेस के साथ संयोजन में विस्तार के तरीके
इंटरफेस के साथ विस्तार विधियों का उपयोग करना बहुत सुविधाजनक है क्योंकि कार्यान्वयन को कक्षा के बाहर संग्रहीत किया जा सकता है और क्लास में कुछ कार्यक्षमता जोड़ने के लिए इंटरफ़ेस के साथ वर्ग को सजाने के लिए है।
public interface IInterface
{
string Do()
}
public static class ExtensionMethods{
public static string DoWith(this IInterface obj){
//does something with IInterface instance
}
}
public class Classy : IInterface
{
// this is a wrapper method; you could also call DoWith() on a Classy instance directly,
// provided you import the namespace containing the extension method
public Do(){
return this.DoWith();
}
}
जैसे उपयोग करें:
var classy = new Classy();
classy.Do(); // will call the extension
classy.DoWith(); // Classy implements IInterface so it can also be called this way
IList विस्तार विधि उदाहरण: 2 सूचियों की तुलना
आप एक ही प्रकार के दो IList <T> उदाहरणों की सामग्री की तुलना करने के लिए निम्नलिखित विस्तार विधि का उपयोग कर सकते हैं।
डिफ़ॉल्ट रूप से आइटमों की सूची के भीतर उनके आदेश के आधार पर तुलना की जाती है और आइटम स्वयं, isOrdered
पैरामीटर के लिए गलत पास करते isOrdered
केवल उन वस्तुओं की तुलना करेंगे जो उनके आदेश की परवाह किए बिना स्वयं करते हैं।
काम करने के लिए इस विधि के लिए, जेनेरिक प्रकार ( T
) को Equals
और GetHashCode
दोनों तरीकों को ओवरराइड करना चाहिए।
उपयोग:
List<string> list1 = new List<string> {"a1", "a2", null, "a3"};
List<string> list2 = new List<string> {"a1", "a2", "a3", null};
list1.Compare(list2);//this gives false
list1.Compare(list2, false);//this gives true. they are equal when the order is disregarded
तरीका:
public static bool Compare<T>(this IList<T> list1, IList<T> list2, bool isOrdered = true)
{
if (list1 == null && list2 == null)
return true;
if (list1 == null || list2 == null || list1.Count != list2.Count)
return false;
if (isOrdered)
{
for (int i = 0; i < list2.Count; i++)
{
var l1 = list1[i];
var l2 = list2[i];
if (
(l1 == null && l2 != null) ||
(l1 != null && l2 == null) ||
(!l1.Equals(l2)))
{
return false;
}
}
return true;
}
else
{
List<T> list2Copy = new List<T>(list2);
//Can be done with Dictionary without O(n^2)
for (int i = 0; i < list1.Count; i++)
{
if (!list2Copy.Remove(list1[i]))
return false;
}
return true;
}
}
गणना के साथ विस्तार विधियाँ
विस्तार विधियों में एन्यूमरेशन्स में कार्यक्षमता जोड़ने के लिए उपयोगी है।
रूपांतरण पद्धति को लागू करने के लिए एक सामान्य उपयोग है।
public enum YesNo
{
Yes,
No,
}
public static class EnumExtentions
{
public static bool ToBool(this YesNo yn)
{
return yn == YesNo.Yes;
}
public static YesNo ToYesNo(this bool yn)
{
return yn ? YesNo.Yes : YesNo.No;
}
}
अब आप अपने एनम मूल्य को जल्दी से एक अलग प्रकार में बदल सकते हैं। इस मामले में एक बूल।
bool yesNoBool = YesNo.Yes.ToBool(); // yesNoBool == true
YesNo yesNoEnum = false.ToYesNo(); // yesNoEnum == YesNo.No
वैकल्पिक रूप से विस्तार विधियों का उपयोग तरीकों की तरह संपत्ति जोड़ने के लिए किया जा सकता है।
public enum Element
{
Hydrogen,
Helium,
Lithium,
Beryllium,
Boron,
Carbon,
Nitrogen,
Oxygen
//Etc
}
public static class ElementExtensions
{
public static double AtomicMass(this Element element)
{
switch(element)
{
case Element.Hydrogen: return 1.00794;
case Element.Helium: return 4.002602;
case Element.Lithium: return 6.941;
case Element.Beryllium: return 9.012182;
case Element.Boron: return 10.811;
case Element.Carbon: return 12.0107;
case Element.Nitrogen: return 14.0067;
case Element.Oxygen: return 15.9994;
//Etc
}
return double.Nan;
}
}
var massWater = 2*Element.Hydrogen.AtomicMass() + Element.Oxygen.AtomicMass();
एक्सटेंशन और इंटरफेस एक साथ DRY कोड और मिक्सिन जैसी कार्यक्षमता को सक्षम करते हैं
एक्सटेंशन विधियाँ आपको इंटरफ़ेस में केवल मुख्य आवश्यक कार्यक्षमता सहित अपनी इंटरफ़ेस परिभाषाओं को सरल बनाने में सक्षम बनाती हैं और आपको विस्तार विधियों के रूप में सुविधा विधियों और अधिभार को परिभाषित करने की अनुमति देती हैं। कम तरीकों के साथ इंटरफेस नई कक्षाओं में लागू करना आसान है। इंटरफ़ेस में उन्हें शामिल करने के बजाय एक्सटेंशन के रूप में अधिभार रखना, सीधे आपको हर कार्यान्वयन में बॉयलरप्लेट कोड को कॉपी करने से बचाता है, जिससे आपको अपना कोड DRY रखने में मदद मिलती है। यह वास्तव में मिक्सिन पैटर्न के समान है जो C # सपोर्ट नहीं करता है।
System.Linq.Enumerable
का विस्तार IEnumerable<T>
इसका एक बेहतरीन उदाहरण है। IEnumerable<T>
केवल दो तरीकों को लागू करने के लिए कार्यान्वयन वर्ग की आवश्यकता है: सामान्य और गैर-जेनेरिक GetEnumerator()
। लेकिन System.Linq.Enumerable
IEnumerable<T>
का संक्षिप्त और स्पष्ट उपभोग सक्षम करने वाले एक्सटेंशन के रूप में अनगिनत उपयोगी उपयोगिताओं प्रदान करता है।
एक्सटेंशन के रूप में दी गई सुविधा अधिभार के साथ एक बहुत ही सरल इंटरफ़ेस है।
public interface ITimeFormatter
{
string Format(TimeSpan span);
}
public static class TimeFormatter
{
// Provide an overload to *all* implementers of ITimeFormatter.
public static string Format(
this ITimeFormatter formatter,
int millisecondsSpan)
=> formatter.Format(TimeSpan.FromMilliseconds(millisecondsSpan));
}
// Implementations only need to provide one method. Very easy to
// write additional implementations.
public class SecondsTimeFormatter : ITimeFormatter
{
public string Format(TimeSpan span)
{
return $"{(int)span.TotalSeconds}s";
}
}
class Program
{
static void Main(string[] args)
{
var formatter = new SecondsTimeFormatter();
// Callers get two method overloads!
Console.WriteLine($"4500ms is rougly {formatter.Format(4500)}");
var span = TimeSpan.FromSeconds(5);
Console.WriteLine($"{span} is formatted as {formatter.Format(span)}");
}
}
विशेष मामलों को संभालने के लिए विस्तार विधियाँ
विस्तार विधियों का उपयोग असभ्य व्यावसायिक नियमों के प्रसंस्करण को "छिपाने" के लिए किया जा सकता है जो अन्यथा / यदि कथनों के साथ कॉलिंग फ़ंक्शन को अव्यवस्थित करने की आवश्यकता होगी। यह विस्तार विधियों के साथ नल को संभालने के लिए समान और अनुरूप है। उदाहरण के लिए,
public static class CakeExtensions
{
public static Cake EnsureTrueCake(this Cake cake)
{
//If the cake is a lie, substitute a cake from grandma, whose cakes aren't as tasty but are known never to be lies. If the cake isn't a lie, don't do anything and return it.
return CakeVerificationService.IsCakeLie(cake) ? GrandmasKitchen.Get1950sCake() : cake;
}
}
Cake myCake = Bakery.GetNextCake().EnsureTrueCake();
myMouth.Eat(myCake);//Eat the cake, confident that it is not a lie.
स्टेटिक विधियों और कॉलबैक के साथ एक्सटेंशन विधियों का उपयोग करना
एक्सटेंशन मेथड्स को उन फंक्शन्स के रूप में इस्तेमाल करने पर विचार करें जो अन्य कोड को लपेटते हैं, यहाँ एक बढ़िया उदाहरण है जो ट्राई कैच कंस्ट्रक्शन को रैप करने के लिए स्टैटिक मेथड और और एक्सटेंशन मेथड दोनों का उपयोग करता है। अपना कोड बुलेट प्रूफ बनाएं ...
using System;
using System.Diagnostics;
namespace Samples
{
/// <summary>
/// Wraps a try catch statement as a static helper which uses
/// Extension methods for the exception
/// </summary>
public static class Bullet
{
/// <summary>
/// Wrapper for Try Catch Statement
/// </summary>
/// <param name="code">Call back for code</param>
/// <param name="error">Already handled and logged exception</param>
public static void Proof(Action code, Action<Exception> error)
{
try
{
code();
}
catch (Exception iox)
{
//extension method used here
iox.Log("BP2200-ERR-Unexpected Error");
//callback, exception already handled and logged
error(iox);
}
}
/// <summary>
/// Example of a logging method helper, this is the extension method
/// </summary>
/// <param name="error">The Exception to log</param>
/// <param name="messageID">A unique error ID header</param>
public static void Log(this Exception error, string messageID)
{
Trace.WriteLine(messageID);
Trace.WriteLine(error.Message);
Trace.WriteLine(error.StackTrace);
Trace.WriteLine("");
}
}
/// <summary>
/// Shows how to use both the wrapper and extension methods.
/// </summary>
public class UseBulletProofing
{
public UseBulletProofing()
{
var ok = false;
var result = DoSomething();
if (!result.Contains("ERR"))
{
ok = true;
DoSomethingElse();
}
}
/// <summary>
/// How to use Bullet Proofing in your code.
/// </summary>
/// <returns>A string</returns>
public string DoSomething()
{
string result = string.Empty;
//Note that the Bullet.Proof method forces this construct.
Bullet.Proof(() =>
{
//this is the code callback
result = "DST5900-INF-No Exceptions in this code";
}, error =>
{
//error is the already logged and handled exception
//determine the base result
result = "DTS6200-ERR-An exception happened look at console log";
if (error.Message.Contains("SomeMarker"))
{
//filter the result for Something within the exception message
result = "DST6500-ERR-Some marker was found in the exception";
}
});
return result;
}
/// <summary>
/// Next step in workflow
/// </summary>
public void DoSomethingElse()
{
//Only called if no exception was thrown before
}
}
}
इंटरफेस पर विस्तार के तरीके
विस्तार विधियों की एक उपयोगी विशेषता यह है कि आप एक इंटरफ़ेस के लिए सामान्य तरीके बना सकते हैं। आम तौर पर एक इंटरफ़ेस में साझा कार्यान्वयन नहीं हो सकता है, लेकिन विस्तार के तरीकों के साथ वे कर सकते हैं।
public interface IVehicle
{
int MilesDriven { get; set; }
}
public static class Extensions
{
public static int FeetDriven(this IVehicle vehicle)
{
return vehicle.MilesDriven * 5028;
}
}
इस उदाहरण में, FeetDriven
का उपयोग किसी भी IVehicle
पर किया जा सकता है। इस पद्धति का यह तर्क सभी IVehicle
s पर लागू होगा, इसलिए इसे इस तरह से किया जा सकता है ताकि IVehicle
परिभाषा में FeetDriven
का होना आवश्यक न हो जिसे सभी बच्चों के लिए समान तरीके से लागू किया जाएगा।
सुंदर मैपर कक्षाएं बनाने के लिए एक्सटेंशन विधियों का उपयोग करना
हम विस्तार विधियों के साथ एक बेहतर मैपर कक्षाएं बना सकते हैं, मान लीजिए कि मेरे पास कुछ डीटीओ कक्षाएं हैं
public class UserDTO
{
public AddressDTO Address { get; set; }
}
public class AddressDTO
{
public string Name { get; set; }
}
और मुझे इसी मॉडल मॉडल वर्गों को देखने की जरूरत है
public class UserViewModel
{
public AddressViewModel Address { get; set; }
}
public class AddressViewModel
{
public string Name { get; set; }
}
तब मैं नीचे की तरह अपना मैपर क्लास बना सकता हूं
public static class ViewModelMapper
{
public static UserViewModel ToViewModel(this UserDTO user)
{
return user == null ?
null :
new UserViewModel()
{
Address = user.Address.ToViewModel()
// Job = user.Job.ToViewModel(),
// Contact = user.Contact.ToViewModel() .. and so on
};
}
public static AddressViewModel ToViewModel(this AddressDTO userAddr)
{
return userAddr == null ?
null :
new AddressViewModel()
{
Name = userAddr.Name
};
}
}
फिर अंत में मैं नीचे की तरह अपने मैपर का आह्वान कर सकता हूं
UserDTO userDTOObj = new UserDTO() {
Address = new AddressDTO() {
Name = "Address of the user"
}
};
UserViewModel user = userDTOObj.ToViewModel(); // My DTO mapped to Viewmodel
यहां की सुंदरता सभी मानचित्रण विधि का एक सामान्य नाम (ToViewModel) है और हम इसे कई तरीकों से पुन: उपयोग कर सकते हैं
नए संग्रह प्रकार (जैसे डिक्लिस्ट) बनाने के लिए एक्सटेंशन विधियों का उपयोग करना
List<T>
मान वाले Dictionary
जैसे नेस्टेड संग्रह के लिए प्रयोज्य में सुधार के लिए आप विस्तार विधियाँ बना सकते हैं।
निम्नलिखित विस्तार विधियों पर विचार करें:
public static class DictListExtensions
{
public static void Add<TKey, TValue, TCollection>(this Dictionary<TKey, TCollection> dict, TKey key, TValue value)
where TCollection : ICollection<TValue>, new()
{
TCollection list;
if (!dict.TryGetValue(key, out list))
{
list = new TCollection();
dict.Add(key, list);
}
list.Add(value);
}
public static bool Remove<TKey, TValue, TCollection>(this Dictionary<TKey, TCollection> dict, TKey key, TValue value)
where TCollection : ICollection<TValue>
{
TCollection list;
if (!dict.TryGetValue(key, out list))
{
return false;
}
var ret = list.Remove(value);
if (list.Count == 0)
{
dict.Remove(key);
}
return ret;
}
}
आप विस्तार विधियों का उपयोग इस प्रकार कर सकते हैं:
var dictList = new Dictionary<string, List<int>>();
dictList.Add("example", 5);
dictList.Add("example", 10);
dictList.Add("example", 15);
Console.WriteLine(String.Join(", ", dictList["example"])); // 5, 10, 15
dictList.Remove("example", 5);
dictList.Remove("example", 10);
Console.WriteLine(String.Join(", ", dictList["example"])); // 15
dictList.Remove("example", 15);
Console.WriteLine(dictList.ContainsKey("example")); // False