C# Language
जेनेरिक्स
खोज…
वाक्य - विन्यास
-
public void SomeMethod <T> () { }
-
public void SomeMethod<T, V>() { }
-
public T SomeMethod<T>(IEnumerable<T> sequence) { ... }
-
public void SomeMethod<T>() where T : new() { }
-
public void SomeMethod<T, V>() where T : new() where V : struct { }
-
public void SomeMethod<T>() where T: IDisposable { }
-
public void SomeMethod<T>() where T: Foo { }
-
public class MyClass<T> { public T Data {get; set; } }
पैरामीटर
पैरामीटर (रों) | विवरण |
---|---|
टी, वी | जेनेरिक घोषणाओं के लिए प्लेसहोल्डर टाइप करें |
टिप्पणियों
C # में जेनरिक रनटाइम के लिए सभी तरह से समर्थित हैं: C # के साथ निर्मित जेनेरिक प्रकारों को CIL के संकलित होने के बाद भी संरक्षित किया जाएगा।
इसका प्रभावी रूप से मतलब है कि, C # में, सामान्य प्रकारों को प्रतिबिंबित करना और उन्हें देखना संभव है क्योंकि वे घोषित किए गए थे या जांचते हैं कि क्या वस्तु उदाहरण के लिए जेनेरिक प्रकार का उदाहरण है। यह प्रकार के उन्मूलन के विपरीत है, जहां संकलन के दौरान सामान्य प्रकार की जानकारी हटा दी जाती है। यह जेनेरिक के लिए टेम्पलेट दृष्टिकोण के विपरीत भी है, जहां कई ठोस सामान्य प्रकार रनटाइम के दौरान कई गैर-जेनेरिक प्रकार बन जाते हैं, और मूल जेनेरिक प्रकार परिभाषाओं को आगे बढ़ाने के लिए आवश्यक किसी भी मेटाडेटा को खो दिया जाता है।
हालांकि, सावधान रहें, जब जेनेरिक प्रकारों को दर्शाते हैं: सामान्य प्रकार के नामों को संकलन पर बदल दिया जाएगा, एंगल्ड कोष्ठक को प्रतिस्थापित किया जाएगा और जेनेरिक प्रकार के मापदंडों की संख्या के बाद एक बैकटिक द्वारा टाइप पैरामीटर के नाम। इस प्रकार, एक Dictionary<TKey, Tvalue>
का अनुवाद Dictionary`2
।
प्रकार पैरामीटर (कक्षाएं)
घोषणा:
class MyGenericClass<T1, T2, T3, ...>
{
// Do something with the type parameters.
}
initialisation:
var x = new MyGenericClass<int, char, bool>();
उपयोग (एक पैरामीटर के प्रकार के रूप में):
void AnotherMethod(MyGenericClass<float, byte, char> arg) { ... }
प्रकार पैरामीटर (तरीके)
घोषणा:
void MyGenericMethod<T1, T2, T3>(T1 a, T2 b, T3 c)
{
// Do something with the type parameters.
}
मंगलाचरण:
एक जेनेटिक विधि के लिए प्रकार के तर्क की आपूर्ति करने की कोई आवश्यकता नहीं है, क्योंकि संकलक प्रकार का अनुमान लगा सकता है।
int x =10;
int y =20;
string z = "test";
MyGenericMethod(x,y,z);
हालांकि, अगर कोई अस्पष्टता है, तो जेनेरिक विधियों को टाइप के रूप में बुलाया जाना चाहिए
MyGenericMethod<int, int, string>(x,y,z);
टाइप पैरामीटर्स (इंटरफेस)
घोषणा:
interface IMyGenericInterface<T1, T2, T3, ...> { ... }
उपयोग (विरासत में):
class ClassA<T1, T2, T3> : IMyGenericInterface<T1, T2, T3> { ... }
class ClassB<T1, T2> : IMyGenericInterface<T1, T2, int> { ... }
class ClassC<T1> : IMyGenericInterface<T1, char, int> { ... }
class ClassD : IMyGenericInterface<bool, char, int> { ... }
उपयोग (एक पैरामीटर के प्रकार के रूप में):
void SomeMethod(IMyGenericInterface<int, char, bool> arg) { ... }
निहित प्रकार का निष्कर्ष (तरीके)
एक सामान्य विधि के लिए औपचारिक तर्क पारित करते समय, प्रासंगिक सामान्य प्रकार के तर्क आमतौर पर अनुमानित रूप से अनुमान लगाए जा सकते हैं। यदि सभी सामान्य प्रकार का अनुमान लगाया जा सकता है, तो उन्हें सिंटैक्स में निर्दिष्ट करना वैकल्पिक है।
निम्नलिखित सामान्य विधि पर विचार करें। इसका एक औपचारिक पैरामीटर और एक सामान्य प्रकार का पैरामीटर है। उनके बीच एक बहुत स्पष्ट संबंध है - जेनेरिक प्रकार पैरामीटर के तर्क के रूप में पारित प्रकार को औपचारिक पैरामीटर को दिए गए तर्क के संकलन-समय प्रकार के समान होना चाहिए।
void M<T>(T obj)
{
}
ये दो कॉल बराबर हैं:
M<object>(new object());
M(new object());
ये दोनों कॉल भी बराबर हैं:
M<string>("");
M("");
और इसलिए ये तीन कॉल हैं:
M<object>("");
M((object) "");
M("" as object);
ध्यान दें कि यदि कम से कम एक प्रकार के तर्क का अनुमान नहीं लगाया जा सकता है, तो उन सभी को निर्दिष्ट करना होगा।
निम्नलिखित सामान्य विधि पर विचार करें। पहला सामान्य प्रकार का तर्क औपचारिक तर्क के प्रकार के समान है। लेकिन दूसरे सामान्य प्रकार के तर्क के लिए ऐसा कोई संबंध नहीं है। इसलिए, कंपाइलर के पास इस पद्धति में किसी भी कॉल में दूसरे जेनेरिक प्रकार के तर्क का उल्लेख करने का कोई तरीका नहीं है।
void X<T1, T2>(T1 obj)
{
}
यह अब काम नहीं करता है:
X("");
यह या तो काम नहीं करता है, क्योंकि कंपाइलर सुनिश्चित नहीं है कि हम पहले या दूसरे जेनेरिक पैरामीटर को निर्दिष्ट कर रहे हैं (दोनों object
लिए मान्य होंगे):
X<object>("");
हमें उन दोनों को टाइप करना होगा, जैसे:
X<string, object>("");
बाधाएं लिखें (कक्षाएं और इंटरफ़ेस)
प्रकार की बाधाएं एक निश्चित इंटरफ़ेस या वर्ग को लागू करने के लिए एक प्रकार के पैरामीटर को बाध्य करने में सक्षम हैं।
interface IType;
interface IAnotherType;
// T must be a subtype of IType
interface IGeneric<T>
where T : IType
{
}
// T must be a subtype of IType
class Generic<T>
where T : IType
{
}
class NonGeneric
{
// T must be a subtype of IType
public void DoSomething<T>(T arg)
where T : IType
{
}
}
// Valid definitions and expressions:
class Type : IType { }
class Sub : IGeneric<Type> { }
class Sub : Generic<Type> { }
new NonGeneric().DoSomething(new Type());
// Invalid definitions and expressions:
class AnotherType : IAnotherType { }
class Sub : IGeneric<AnotherType> { }
class Sub : Generic<AnotherType> { }
new NonGeneric().DoSomething(new AnotherType());
कई बाधाओं के लिए सिंटैक्स:
class Generic<T, T1>
where T : IType
where T1 : Base, new()
{
}
प्रकार की बाधाएं वंशानुक्रम के समान कार्य करती हैं, जिसमें सामान्य प्रकार पर बाधाओं के रूप में कई इंटरफेस निर्दिष्ट करना संभव है, लेकिन केवल एक वर्ग:
class A { /* ... */ }
class B { /* ... */ }
interface I1 { }
interface I2 { }
class Generic<T>
where T : A, I1, I2
{
}
class Generic2<T>
where T : A, B //Compilation error
{
}
एक और नियम यह है कि वर्ग को पहले बाधा और फिर इंटरफेस के रूप में जोड़ा जाना चाहिए:
class Generic<T>
where T : A, I1
{
}
class Generic2<T>
where T : I1, A //Compilation error
{
}
सभी घोषित बाधाओं को काम करने के लिए एक विशेष सामान्य तात्कालिकता के लिए एक साथ संतुष्ट होना चाहिए। बाधाओं के दो या अधिक वैकल्पिक सेटों को निर्दिष्ट करने का कोई तरीका नहीं है।
प्रकार की कमी (वर्ग और संरचना)
यह निर्दिष्ट करना संभव है कि संबंधित बाधा class
या struct
का उपयोग करके प्रकार का तर्क संदर्भ प्रकार या मान प्रकार होना चाहिए या नहीं। यदि इन बाधाओं का उपयोग किया जाता है, तो उन्हें अन्य सभी बाधाओं से पहले परिभाषित किया जाना चाहिए (उदाहरण के लिए एक मूल प्रकार या new()
) सूचीबद्ध किया जा सकता है।
// TRef must be a reference type, the use of Int32, Single, etc. is invalid.
// Interfaces are valid, as they are reference types
class AcceptsRefType<TRef>
where TRef : class
{
// TStruct must be a value type.
public void AcceptStruct<TStruct>()
where TStruct : struct
{
}
// If multiple constraints are used along with class/struct
// then the class or struct constraint MUST be specified first
public void Foo<TComparableClass>()
where TComparableClass : class, IComparable
{
}
}
बाधाएं टाइप करें (नया-कीवर्ड)
new()
अवरोध का उपयोग करके, खाली (डिफ़ॉल्ट) कंस्ट्रक्टर को परिभाषित करने के लिए प्रकार के मापदंडों को लागू करना संभव है।
class Foo
{
public Foo () { }
}
class Bar
{
public Bar (string s) { ... }
}
class Factory<T>
where T : new()
{
public T Create()
{
return new T();
}
}
Foo f = new Factory<Foo>().Create(); // Valid.
Bar b = new Factory<Bar>().Create(); // Invalid, Bar does not define a default/empty constructor.
बनाने के लिए दूसरा कॉल Create()
निम्नलिखित संदेश के साथ संकलन समय त्रुटि देगा:
जेनेरिक प्रकार या विधि 'फ़ैक्टरी' में पैरामीटर 'T' के रूप में इसका उपयोग करने के लिए 'Bar' एक सार्वजनिक पैरामीटर रहित निर्माता के साथ एक गैर-अमूर्त प्रकार होना चाहिए।
मापदंडों के साथ एक निर्माता के लिए कोई बाधा नहीं है, केवल पैरामीटर रहित निर्माणकर्ता समर्थित हैं।
प्रकार की अनुमान (कक्षाएं)
डेवलपर्स को इस तथ्य से पकड़ा जा सकता है कि निर्माणकर्ताओं के लिए प्रकार का अनुमान काम नहीं करता है :
class Tuple<T1,T2>
{
public Tuple(T1 value1, T2 value2)
{
}
}
var x = new Tuple(2, "two"); // This WON'T work...
var y = new Tuple<int, string>(2, "two"); // even though the explicit form will.
स्पष्ट रूप से निर्दिष्ट मापदंडों के बिना उदाहरण बनाने का पहला तरीका संकलन समय त्रुटि का कारण होगा जो कहेगा:
जेनेरिक प्रकार 'Tuple <T1, T2>' के प्रयोग के लिए 2 प्रकार के तर्कों की आवश्यकता होती है
एक सामान्य वर्कअराउंड स्टैटिक क्लास में एक सहायक विधि जोड़ना है:
static class Tuple
{
public static Tuple<T1, T2> Create<T1, T2>(T1 value1, T2 value2)
{
return new Tuple<T1, T2>(value1, value2);
}
}
var x = Tuple.Create(2, "two"); // This WILL work...
प्रकार के मापदंडों पर चिंतन करना
typeof
ऑपरेटर प्रकार के मापदंडों पर काम करता है।
class NameGetter<T>
{
public string GetTypeName()
{
return typeof(T).Name;
}
}
स्पष्ट प्रकार के पैरामीटर
अलग-अलग मामले हैं जहां आपको स्पष्ट रूप से एक सामान्य विधि के प्रकार के मापदंडों को निर्दिष्ट करना होगा। नीचे दिए गए दोनों मामलों में, कंपाइलर निर्दिष्ट विधि मापदंडों से सभी प्रकार के मापदंडों का अनुमान लगाने में सक्षम नहीं है।
एक मामला है जब कोई पैरामीटर नहीं हैं:
public void SomeMethod<T, V>()
{
// No code for simplicity
}
SomeMethod(); // doesn't compile
SomeMethod<int, bool>(); // compiles
दूसरा मामला तब होता है जब प्रकार पैरामीटर में से एक (या अधिक) विधि मापदंडों का हिस्सा नहीं होता है:
public K SomeMethod<K, V>(V input)
{
return default(K);
}
int num1 = SomeMethod(3); // doesn't compile
int num2 = SomeMethod<int>("3"); // doesn't compile
int num3 = SomeMethod<int, string>("3"); // compiles.
एक बाधा प्रकार के रूप में एक अंतरफलक के साथ सामान्य विधि का उपयोग करना।
यह क्लास एनिमल पर ईट पद्धति के अंदर जेनेरिक प्रकार TFood का उपयोग करने के तरीके का एक उदाहरण है
public interface IFood
{
void EatenBy(Animal animal);
}
public class Grass: IFood
{
public void EatenBy(Animal animal)
{
Console.WriteLine("Grass was eaten by: {0}", animal.Name);
}
}
public class Animal
{
public string Name { get; set; }
public void Eat<TFood>(TFood food)
where TFood : IFood
{
food.EatenBy(this);
}
}
public class Carnivore : Animal
{
public Carnivore()
{
Name = "Carnivore";
}
}
public class Herbivore : Animal, IFood
{
public Herbivore()
{
Name = "Herbivore";
}
public void EatenBy(Animal animal)
{
Console.WriteLine("Herbivore was eaten by: {0}", animal.Name);
}
}
आप ईट विधि को इस तरह कह सकते हैं:
var grass = new Grass();
var sheep = new Herbivore();
var lion = new Carnivore();
sheep.Eat(grass);
//Output: Grass was eaten by: Herbivore
lion.Eat(sheep);
//Output: Herbivore was eaten by: Carnivore
इस स्थिति में यदि आप कॉल करने का प्रयास करते हैं:
sheep.Eat(lion);
यह संभव नहीं होगा क्योंकि ऑब्जेक्ट शेर, IFood इंटरफ़ेस को लागू नहीं करता है। उपरोक्त कॉल करने का प्रयास एक संकलक त्रुटि उत्पन्न करेगा: "सामान्य प्रकार या विधि 'Animal.Eat (TFood)' में टाइप 'कार्निवोर' का उपयोग पैरामीटर 'TFood' के रूप में नहीं किया जा सकता है। इसका कोई अंतर्निहित संदर्भ रूपांतरण नहीं है। कार्निवोर 'टू' इफूड '। "
सहप्रसरण
कब एक IEnumerable<T>
एक अलग IEnumerable<T1>
का उपप्रकार है? जब T
, T1
का एक उपप्रकार है। IEnumerable
अपने T
पैरामीटर में सहसंयोजक है, जिसका अर्थ है कि IEnumerable
का उपप्रकार संबंध T
के समान दिशा में जाता है।
class Animal { /* ... */ }
class Dog : Animal { /* ... */ }
IEnumerable<Dog> dogs = Enumerable.Empty<Dog>();
IEnumerable<Animal> animals = dogs; // IEnumerable<Dog> is a subtype of IEnumerable<Animal>
// dogs = animals; // Compilation error - IEnumerable<Animal> is not a subtype of IEnumerable<Dog>
किसी दिए गए प्रकार के पैरामीटर के साथ सहसंयोजक सामान्य प्रकार का उदाहरण कम व्युत्पन्न प्रकार के पैरामीटर के साथ समान जेनेरिक प्रकार के लिए परिवर्तनीय है।
यह संबंध इसलिए है क्योंकि IEnumerable
T
s का उत्पादन करता है लेकिन उनका उपभोग नहीं करता है। Dog
एस का उत्पादन करने वाली एक वस्तु का उपयोग इस तरह किया जा सकता है जैसे कि वह Animal
एस उत्पन्न करता है।
Covariant प्रकार के पैरामीटर out
कीवर्ड का उपयोग करके घोषित किए जाते हैं, क्योंकि पैरामीटर का उपयोग केवल आउटपुट के रूप में किया जाना चाहिए।
interface IEnumerable<out T> { /* ... */ }
एक प्रकार का पैरामीटर जिसे सहसंयोजक के रूप में घोषित किया गया है, इनपुट के रूप में प्रकट नहीं हो सकता है।
interface Bad<out T>
{
void SetT(T t); // type error
}
यहाँ एक पूर्ण उदाहरण है:
using NUnit.Framework;
namespace ToyStore
{
enum Taste { Bitter, Sweet };
interface IWidget
{
int Weight { get; }
}
interface IFactory<out TWidget>
where TWidget : IWidget
{
TWidget Create();
}
class Toy : IWidget
{
public int Weight { get; set; }
public Taste Taste { get; set; }
}
class ToyFactory : IFactory<Toy>
{
public const int StandardWeight = 100;
public const Taste StandardTaste = Taste.Sweet;
public Toy Create() { return new Toy { Weight = StandardWeight, Taste = StandardTaste }; }
}
[TestFixture]
public class GivenAToyFactory
{
[Test]
public static void WhenUsingToyFactoryToMakeWidgets()
{
var toyFactory = new ToyFactory();
//// Without out keyword, note the verbose explicit cast:
// IFactory<IWidget> rustBeltFactory = (IFactory<IWidget>)toyFactory;
// covariance: concrete being assigned to abstract (shiny and new)
IFactory<IWidget> widgetFactory = toyFactory;
IWidget anotherToy = widgetFactory.Create();
Assert.That(anotherToy.Weight, Is.EqualTo(ToyFactory.StandardWeight)); // abstract contract
Assert.That(((Toy)anotherToy).Taste, Is.EqualTo(ToyFactory.StandardTaste)); // concrete contract
}
}
}
contravariance
IComparer<T>
एक अलग IComparer<T1>
का उपप्रकार कब है? जब T1
T
का एक उपप्रकार है। IComparer
अपने में contravariant है T
पैरामीटर, जिसका अर्थ है कि IComparer
के उप प्रकार संबंधों के रूप में विपरीत दिशा में चला जाता है T
एस '।
class Animal { /* ... */ }
class Dog : Animal { /* ... */ }
IComparer<Animal> animalComparer = /* ... */;
IComparer<Dog> dogComparer = animalComparer; // IComparer<Animal> is a subtype of IComparer<Dog>
// animalComparer = dogComparer; // Compilation error - IComparer<Dog> is not a subtype of IComparer<Animal>
एक दिए गए प्रकार के पैरामीटर के साथ एक कॉन्ट्रावेरिएंट जेनेरिक प्रकार का एक उदाहरण समान व्युत्पन्न प्रकार के साथ एक ही सामान्य प्रकार के लिए परिवर्तनीय है।
यह संबंध इसलिए है क्योंकि IComparer
T
s का उपभोग करता है लेकिन उनका उत्पादन नहीं करता है। एक वस्तु जो किसी भी दो Animal
तुलना कर सकती है, का उपयोग दो Dog
एस की तुलना करने के लिए किया जा सकता है।
कीवर्ड में कॉन्ट्रावेरिएंट प्रकार के पैरामीटर का उपयोग करने की घोषणा की in
, क्योंकि पैरामीटर को केवल इनपुट के रूप में उपयोग किया जाना चाहिए।
interface IComparer<in T> { /* ... */ }
एक प्रकार का पैरामीटर जो कंट्रावेरेंट के रूप में घोषित किया गया है, वह आउटपुट के रूप में प्रकट नहीं हो सकता है।
interface Bad<in T>
{
T GetT(); // type error
}
निश्चरता
IList<T>
एक अलग IList<T1>
का उपप्रकार नहीं है। IList
अपने प्रकार के पैरामीटर में अपरिवर्तनीय है।
class Animal { /* ... */ }
class Dog : Animal { /* ... */ }
IList<Dog> dogs = new List<Dog>();
IList<Animal> animals = dogs; // type error
सूचियों के लिए कोई उपप्रकार संबंध नहीं है क्योंकि आप किसी सूची में मान रख सकते हैं और सूची से मान निकाल सकते हैं।
यदि IList
covariant था, तो आप किसी दिए गए सूची में गलत उपप्रकार की आइटम जोड़ सकेंगे।
IList<Animal> animals = new List<Dog>(); // supposing this were allowed...
animals.Add(new Giraffe()); // ... then this would also be allowed, which is bad!
यदि IList
contravariant था, तो आप दी गई सूची से गलत उपप्रकार के मान निकाल सकते हैं।
IList<Dog> dogs = new List<Animal> { new Dog(), new Giraffe() }; // if this were allowed...
Dog dog = dogs[1]; // ... then this would be allowed, which is bad!
इनवाइट प्रकार पैरामीटर को कीवर्ड के in
और out
दोनों को छोड़ कर घोषित किया out
है।
interface IList<T> { /* ... */ }
वैरिएंट इंटरफेस
इंटरफ़ेस में भिन्न प्रकार के पैरामीटर हो सकते हैं।
interface IEnumerable<out T>
{
// ...
}
interface IComparer<in T>
{
// ...
}
लेकिन कक्षाएं और संरचनाएं नहीं हो सकती हैं
class BadClass<in T1, out T2> // not allowed
{
}
struct BadStruct<in T1, out T2> // not allowed
{
}
न ही जेनेरिक विधि घोषणाएं
class MyClass
{
public T Bad<out T, in T1>(T1 t1) // not allowed
{
// ...
}
}
नीचे दिया गया उदाहरण एक ही इंटरफ़ेस पर कई विचरण घोषणाओं को दर्शाता है
interface IFoo<in T1, out T2, T3>
// T1 : Contravariant type
// T2 : Covariant type
// T3 : Invariant type
{
// ...
}
IFoo<Animal, Dog, int> foo1 = /* ... */;
IFoo<Dog, Animal, int> foo2 = foo1;
// IFoo<Animal, Dog, int> is a subtype of IFoo<Dog, Animal, int>
वेरिएंट प्रतिनिधियों
प्रतिनिधि प्रकार प्रकार के पैरामीटर हो सकते हैं।
delegate void Action<in T>(T t); // T is an input
delegate T Func<out T>(); // T is an output
delegate T2 Func<in T1, out T2>(); // T1 is an input, T2 is an output
यह लिसकोव प्रतिस्थापन सिद्धांत से आता है , जो बताता है कि (अन्य बातों के अलावा) एक विधि डी को एक विधि से अधिक व्युत्पन्न माना जा सकता है:
- D में B के बराबर या अधिक व्युत्पन्न रिटर्न प्रकार है
- D में B के बराबर या उससे अधिक सामान्य संगत पैरामीटर प्रकार हैं
इसलिए निम्नलिखित कार्य सभी प्रकार के सुरक्षित हैं:
Func<object, string> original = SomeMethod;
Func<object, object> d1 = original;
Func<string, string> d2 = original;
Func<string, object> d3 = original;
विभिन्न प्रकार के पैरामीटर और रिटर्न मान
यदि कोई सहसंयोजक प्रकार आउटपुट के रूप में प्रकट होता है, तो युक्त प्रकार सहसंयोजक होता है। T
s के निर्माता का उत्पादन T
s के उत्पादन की तरह है।
interface IReturnCovariant<out T>
{
IEnumerable<T> GetTs();
}
यदि एक कंट्राविरेंट प्रकार एक आउटपुट के रूप में प्रकट होता है, तो युक्त प्रकार कॉन्ट्रैवियर होता है। के एक उपभोक्ता उत्पादन T
एस लेने की तरह है T
एस।
interface IReturnContravariant<in T>
{
IComparer<T> GetTComparer();
}
यदि एक सहसंयोजक प्रकार एक इनपुट के रूप में प्रकट होता है, तो युक्त प्रकार विपरीत होता है। के एक निर्माता उपभोक्ता T
रों लेने की तरह है T
एस।
interface IAcceptCovariant<in T>
{
void ProcessTs(IEnumerable<T> ts);
}
यदि एक कंट्राविरेंट प्रकार एक इनपुट के रूप में प्रकट होता है, तो युक्त प्रकार सहसंयोजक होता है। T
s के उपभोक्ता का उत्पादन T
s के उत्पादन की तरह है।
interface IAcceptContravariant<out T>
{
void CompareTs(IComparer<T> tComparer);
}
जेनेरिक मूल्यों की समानता की जाँच करना।
यदि जेनेरिक वर्ग या विधि के तर्क के लिए जेनेरिक प्रकार के मानों की समानता की जांच करना आवश्यक है, तो EqualityComparer<TType>.Default
गुण का उपयोग करें।
public void Foo<TBar>(TBar arg1, TBar arg2)
{
var comparer = EqualityComparer<TBar>.Default;
if (comparer.Equals(arg1,arg2)
{
...
}
}
यह दृष्टिकोण केवल Object.Equals()
विधि को कॉल करने से बेहतर है, क्योंकि डिफ़ॉल्ट तुलना कार्यान्वयन कार्यान्वयन की जांच करता है, चाहे TBar
प्रकार IEquatale<TBar>
इंटरफ़ेस को लागू करता है और यदि हाँ, IEquatable<TBar>.Equals(TBar other)
विधि को कॉल IEquatable<TBar>.Equals(TBar other)
। इससे मूल्य प्रकारों की मुक्केबाजी / अनबॉक्सिंग से बचने की अनुमति मिलती है।
जेनेरिक टाइप कास्टिंग
/// <summary>
/// Converts a data type to another data type.
/// </summary>
public static class Cast
{
/// <summary>
/// Converts input to Type of default value or given as typeparam T
/// </summary>
/// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
/// <param name="input">Input that need to be converted to specified type</param>
/// <param name="defaultValue">defaultValue will be returned in case of value is null or any exception occures</param>
/// <returns>Input is converted in Type of default value or given as typeparam T and returned</returns>
public static T To<T>(object input, T defaultValue)
{
var result = defaultValue;
try
{
if (input == null || input == DBNull.Value) return result;
if (typeof (T).IsEnum)
{
result = (T) Enum.ToObject(typeof (T), To(input, Convert.ToInt32(defaultValue)));
}
else
{
result = (T) Convert.ChangeType(input, typeof (T));
}
}
catch (Exception ex)
{
Tracer.Current.LogException(ex);
}
return result;
}
/// <summary>
/// Converts input to Type of typeparam T
/// </summary>
/// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
/// <param name="input">Input that need to be converted to specified type</param>
/// <returns>Input is converted in Type of default value or given as typeparam T and returned</returns>
public static T To<T>(object input)
{
return To(input, default(T));
}
}
उपयोगों:
std.Name = Cast.To<string>(drConnection["Name"]);
std.Age = Cast.To<int>(drConnection["Age"]);
std.IsPassed = Cast.To<bool>(drConnection["IsPassed"]);
// Casting type using default value
//Following both ways are correct
// Way 1 (In following style input is converted into type of default value)
std.Name = Cast.To(drConnection["Name"], "");
std.Marks = Cast.To(drConnection["Marks"], 0);
// Way 2
std.Name = Cast.To<string>(drConnection["Name"], "");
std.Marks = Cast.To<int>(drConnection["Marks"], 0);
सामान्य प्रकार की कास्टिंग के साथ कॉन्फ़िगरेशन रीडर
/// <summary>
/// Read configuration values from app.config and convert to specified types
/// </summary>
public static class ConfigurationReader
{
/// <summary>
/// Get value from AppSettings by key, convert to Type of default value or typeparam T and return
/// </summary>
/// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
/// <param name="strKey">key to find value from AppSettings</param>
/// <param name="defaultValue">defaultValue will be returned in case of value is null or any exception occures</param>
/// <returns>AppSettings value against key is returned in Type of default value or given as typeparam T</returns>
public static T GetConfigKeyValue<T>(string strKey, T defaultValue)
{
var result = defaultValue;
try
{
if (ConfigurationManager.AppSettings[strKey] != null)
result = (T)Convert.ChangeType(ConfigurationManager.AppSettings[strKey], typeof(T));
}
catch (Exception ex)
{
Tracer.Current.LogException(ex);
}
return result;
}
/// <summary>
/// Get value from AppSettings by key, convert to Type of default value or typeparam T and return
/// </summary>
/// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
/// <param name="strKey">key to find value from AppSettings</param>
/// <returns>AppSettings value against key is returned in Type given as typeparam T</returns>
public static T GetConfigKeyValue<T>(string strKey)
{
return GetConfigKeyValue(strKey, default(T));
}
}
उपयोगों:
var timeOut = ConfigurationReader.GetConfigKeyValue("RequestTimeout", 2000);
var url = ConfigurationReader.GetConfigKeyValue("URL", "www.someurl.com");
var enabled = ConfigurationReader.GetConfigKeyValue("IsEnabled", false);