2011-02-07 12 views
7
public class ConfigControlBase<T> : UserControl 
    where T : ProviderBase 
{ 
    public T Provider { get; set; } 

    public void Init(T provider) 
    { 
     this.Provider = provider; 
    } 
} 


public abstract class ProviderBase 
{ 
    public abstract ConfigControlBase<ProviderBase> GetControl(); 
} 

public class ProviderXConfigControl : ConfigControlBase<ProviderX> 
{ 
} 

public class ProviderX : ProviderBase 
{ 
    public override ConfigControlBase<ProviderBase> GetControl() 
    { 
     var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>; 
     return confControl; 
    } 
} 

return confControl; एक अपवाद फेंकता है:एक सामान्य तत्व प्रकार कास्टिंग नीचे की ओर

परोक्ष ConfigControlBase<ProviderBase>

उत्तर

21

के अपने वर्गों और गुण का नाम बदलने, लेकिन आकार में एक ही रखें:

public class Cage<T> where T : Animal 
{ 
    public T Contents { get; set; } 
} 

public class Aquarium : Cage<Fish> { } 

public abstract class Animal 
{ 
    public abstract Cage<Animal> GetCage(); 
} 

public class Fish : Animal 
{ 
    public override Cage<Animal> GetCage() 
    { 
     return (Cage<Animal>)(new Aquarium()); 
    } 
} 

अब है यह स्पष्ट है कि यह कानूनी क्यों नहीं है? मान लीजिए कि यह कानूनी था। फिर आप यह कर सकते हैं:

Fish fish = new Fish(); 
Cage<Animal> cage = fish.GetCage(); 
cage.contents = new Tiger(); 

और अब आपके एक्वैरियम में बाघ है। और कोई भी उसे नहीं चाहता है।

संकलक (या क्रम) इस प्रकार की त्रुटि को रोकने के लिए किसी भी तरह है, यह जितनी जल्दी हो सके इसे रोकने के लिए चुनता है। यह जल्द से जल्द एक्वेरियम से Cage<Animal> में रूपांतरण के लिए टाइप टेस्ट पर कर सकता है। संकलक जानता है कि यह अंततः एक्वैरियम में बाघों का कारण बन सकता है, इसलिए यह रूपांतरण की अनुमति नहीं देता है। यदि आप कंपाइलर को इसे कास्ट के माध्यम से अनुमति देने के लिए मजबूर करते हैं तो यह रनटाइम पर विफल रहता है।

+8

कमाल रूपक। मेरी इच्छा है कि एमएसडीएन ऐसा ही था। –

1

के प्रकार ConfigControlBase<ProviderX> कनवर्ट नहीं कर सकता इसका कारण यह है ConfigControlBase<ProviderX> एक ConfigControlBase<ProviderBase>

0

अपने

नहीं है
public override ConfigControlBase<ProviderBase> GetControl() 

आबंटित प्रकार तर्क के साथ

var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>; 
8

जेनेरिक प्रकार से मेल नहीं खाता खुद को आबंटित नहीं कर रहे हैं।
उदाहरण के लिए, आप List<string> से List<object> पर नहीं डाले जा सकते हैं, हालांकि stringobject है।

यह तुरंत स्पष्ट नहीं है क्यों इस तरह के कास्टिंग इसलिए समर्थित नहीं है मैं आपको एक उदाहरण देता हूँ:

var words = new List<string> { "Serve God", "love me", "mend" }; 
var objects = (List<object>) words; // C# compiler wouldn't allow this 
objects.Add (new Car()); // we just added a Car to Shakespeare's work and the universe exploded 

सी #, ब्रह्मांड विस्फोट प्रोत्साहित नहीं करता है लेकिन जब से सी # 4.0 इस विचार का एक हल्का संस्करण कार्यान्वित किया जाता है । आप देखते हैं, कुछ मामलों में इस तरह का कास्टिंग वास्तव में सुरक्षित होगा।

.NET 4.0 जेनेरिक में केवल इंटरफेस और प्रतिनिधियों के लिए कॉन्वर्सिस और contravariance की अवधारणाओं को लाता है, तो आप इसे देखना चाह सकते हैं।

उदाहरण (काम नहीं करता है .NET 4.0 से पहले):

void HandleCollection (IEnumerable<object> collection) 
{ 
    // ... 
} 

var words = new List<string> { "Serve God", "love me", "mend" }; 

// IEnumerable is defined as IEnumerable<out T> in .NET 4.0 
// 'out' keyword guarantees that T is only used for return values 
// and therefore client code can't explode the universe 

var objects = (IEnumerable<object>) words; 
HandleCollection (objects); 
+4

"सी # ब्रह्मांड विस्फोट को प्रोत्साहित नहीं करता है" मेरा नया आदर्श वाक्य है। –

+1

ध्यान दें कि * कॉन्वर्सिस मूल्य प्रकारों पर काम नहीं करता है *। आप int की एक सूची को IENumerable में परिवर्तित नहीं कर सकते क्योंकि बॉक्सिंग के लिए स्मृति आवंटित की जानी चाहिए, और ऐसा करने के लिए कोई कोड उत्सर्जित नहीं है। आप स्ट्रिंग की सूची को IENumerable में परिवर्तित कर सकते हैं, क्योंकि स्ट्रिंग एक संदर्भ प्रकार है। –

+0

मैंने स्थिरता बनाए रखने के लिए उदाहरणों को 'स्ट्रिंग' में बदल दिया। टिप्पणी के लिए धन्यवाद। –

0

यह उत्तर आपके परिदृश्य में उपयोगी नहीं हो सकता है, क्योंकि आपको शायद किसी अन्य समाधान की तलाश करनी चाहिए, लेकिन प्रतिबिंब के दौरान मुझे कम सामान्य प्रकारों को बहुत उपयोगी पर डालने की क्षमता मिली, इसलिए मैंने इसके लिए एक समाधान लिखा। यह केवल इंटरफेस के लिए काम करता है, और आपको यह गारंटी देना होगा कि आप केवल सही प्रकार की ऑब्जेक्ट्स को इंटरफेस में पास करेंगे।

मैं मूल रूप से रनटाइम पर प्रॉक्सी क्लास उत्पन्न करता हूं जो आपके लिए आवश्यक सभी आवश्यक बनाता है।

object validator; // An object known to implement IValidation<T>. 
object toValidate; // The object which can be validated by using the validator. 

// Assume validator is IValidation<string> and toValidate a string. 

IValidation<object> validation 
    = Proxy.CreateGenericInterfaceWrapper<IValidation<object>>(validator); 

validation.IsValid(toValidate); // This works! No need to know about the type. 

// The following will throw an InvalidCastException. 
//validation.IsValid(10); 

अधिक जानकारी और स्रोत कोड पाया जा सकता है on my blog: यह के उपयोग के रूप में निम्नानुसार लग रहा है।

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