2009-04-08 18 views
162

यदि BaseFruit में कोई ऐसा कन्स्ट्रक्टर है जो int weight स्वीकार करता है, तो क्या मैं इस तरह की सामान्य विधि में फल का एक टुकड़ा तुरंत कर सकता हूं?जेनेरिक प्रकार का उदाहरण बनाएं?

public void AddFruit<T>()where T: BaseFruit{ 
    BaseFruit fruit = new T(weight); /*new Apple(150);*/ 
    fruit.Enlist(fruitManager); 
} 

टिप्पणियों के पीछे एक उदाहरण जोड़ा गया है। ऐसा लगता है कि मैं केवल यह कर सकता हूं अगर मैं BaseFruit एक पैरामीटर रहित कन्स्ट्रक्टर देता हूं और फिर सदस्य चर के माध्यम से सबकुछ भरता हूं। मेरे असली कोड में (फल के बारे में नहीं) यह बल्कि अव्यवहारिक है।

-Update-
तो यह यह तो किसी भी तरह से कमी से हल नहीं किया जा सकता है।

  • फैक्टरी पैटर्न
  • प्रतिबिंब
  • उत्प्रेरक

मुझे लगता है कि करने के लिए प्रतिबिंब कम से कम साफ है करते हैं, लेकिन मैं के बीच तय नहीं कर सकते हैं: जवाब से तीन उम्मीदवार समाधान कर रहे हैं अन्य दो।

+0

बीटीडब्लू: आज मैं इसे पसंद के आईओसी पुस्तकालय के साथ हल कर दूंगा। –

उत्तर

238

एक सरल उदाहरण इसके अतिरिक्त:

return (T)Activator.CreateInstance(typeof(T), new object[] { weight }); 

ध्यान दें कि टी पर नई() बाधा का उपयोग कर ही है संकलक बनाने के लिए संकलन समय पर सार्वजनिक पैरामीटर रहित कन्स्ट्रक्टर की जांच करें, प्रकार बनाने के लिए उपयोग किया जाने वाला वास्तविक कोड एक्टिवेटर क्लास है।

आपको मौजूदा विशिष्ट निर्माता के बारे में स्वयं को सुनिश्चित करने की आवश्यकता होगी, और इस तरह की आवश्यकता कोड गंध हो सकती है (या इसके बजाय आपको सी # पर वर्तमान संस्करण से बचने की कोशिश करनी चाहिए)।

+0

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

+3

यह एक ठीक काम किया। CreateInstance () प्रक्रिया भी है, लेकिन इसमें कुछ राजन के पैरामीटर के लिए ओवरलोड नहीं है .. –

+8

'नई ऑब्जेक्ट [] {weight} 'का उपयोग करने की कोई आवश्यकता नहीं है। 'CreateInstance' को पैराम्स के साथ घोषित किया गया है,' सार्वजनिक स्थैतिक ऑब्जेक्ट CreateInstance (टाइप टाइप, पैराम्स ऑब्जेक्ट [] args) ', तो आप केवल' रिटर्न (टी) एक्टिवेटर कर सकते हैं। क्रिएट इंस्टेंस (टाइपोफ (टी), वेट); '। यदि कई पैरामीटर हैं, तो उन्हें अलग तर्क के रूप में पास करें। केवल तभी जब आपके पास पहले से ही पैरामीटर का निर्माण किया गया हो, तो क्या आपको इसे 'ऑब्जेक्ट []' में परिवर्तित करने के लिए परेशान होना चाहिए और इसे 'CreateInstance'' में पास करना होगा। – ErikE

40

हां; बदलने के अपने जहां होने के लिए:

where T:BaseFruit, new() 

हालांकि, यह केवल parameterless कंस्ट्रक्टर्स के साथ काम करता है। आपको अपनी संपत्ति को सेट करने के कुछ अन्य साधन होंगे (संपत्ति को स्वयं या कुछ समान स्थापित करना)।

+42

डारन, मुझे रॉबिन्सन मिला ... –

+3

@ जोन स्कीट: यह मुझे ज़ोर से हंसने के लिए बहुत करीब आया (काम पर!)। –

+1

@MichaelMyers मैं _that lucky_ – ppeterka

74

आप किसी भी पैरामीटरयुक्त कन्स्ट्रक्टर का उपयोग नहीं कर सकते हैं। यदि आपके पास "where T : new()" बाधा है तो आप पैरामीटर रहित कन्स्ट्रक्टर का उपयोग कर सकते हैं।

यह एक दर्द है, लेकिन इस तरह के जीवन है :(

यह बातें मैं "static interfaces" साथ संबोधित करना चाहेंगे से एक है। तो आप टी विवश करने के लिए स्थिर तरीकों, ऑपरेटरों और निर्माताओं के शामिल करने के लिए सक्षम होगा , और फिर उन्हें कहते हैं।

+1

नहीं था आप सभी फल हैं! :) –

+10

मैं वास्तव में बस स्कीट कहना चाहता था। –

+1

कम से कम आप ऐसी बाधाएं कर सकते हैं - जावा हमेशा मुझे निराश करता है। –

13

जॉन के रूप में कहा यह एक गैर parameterless निर्माता बाधित के लिए जीवन है। हालांकि एक अलग समाधान एक कारखाने पैटर्न का उपयोग करने के लिए है। यह आसानी से constrainable एक और विकल्प है

interface IFruitFactory<T> where T : BaseFruit { 
    T Create(int weight); 
} 

public void AddFruit<T>(IFruitFactory<T> factory) where T: BaseFruit {  
    BaseFruit fruit = factory.Create(weight); /*new Apple(150);*/  
    fruit.Enlist(fruitManager); 
} 

फिर भी उपयोग करना है एक कार्यात्मक दृष्टिकोण। एक कारखाने विधि में पास करें।

public void AddFruit<T>(Func<int,T> factoryDel) where T : BaseFruit { 
    BaseFruit fruit = factoryDel(weight); /* new Apple(150); */ 
    fruit.Enlist(fruitManager); 
} 
+2

अच्छा सुझाव - यद्यपि यदि आप सावधान नहीं हैं, तो आप जावा डोम एपीआई के नरक में समाप्त हो सकते हैं, कारखानों के साथ गैलरी :( –

+0

@ जोन, वह नहीं चाहते हैं :) – JaredPar

+0

हां, यह एक समाधान है जिसे मैं संयोजित कर रहा था खुद। लेकिन मैं बाधाओं की रेखा में कुछ की उम्मीद कर रहा था। तब अनुमान न करें .. –

10

आप प्रतिबिंब का उपयोग करके कर सकते हैं:

public void AddFruit<T>()where T: BaseFruit 
{ 
    ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); 
    if (constructor == null) 
    { 
    throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); 
    } 
    BaseFruit fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; 
    fruit.Enlist(fruitManager); 
} 

संपादित करें: जोड़ा निर्माता == बातिल की जांच।

संपादित करें: एक तेजी से संस्करण एक कैश का उपयोग कर:

public void AddFruit<T>()where T: BaseFruit 
{ 
    var constructor = FruitCompany<T>.constructor; 
    if (constructor == null) 
    { 
    throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); 
    } 
    var fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; 
    fruit.Enlist(fruitManager); 
} 
private static class FruitCompany<T> 
{ 
    public static readonly ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); 
} 
+0

पर मौजूद नहीं है, हालांकि मुझे प्रतिबिंब के ऊपरी हिस्से को पसंद नहीं है, जैसा कि अन्य ने समझाया है, यह वही तरीका है वर्तमान में। यह देखकर कि इस कन्स्ट्रक्टर को कितना नहीं कहा जाएगा, मैं इसके साथ जा सकता हूं। या कारखाना। अभी तक नहीं पता –

17

सबसे सरल उपाय Activator.CreateInstance<T>()

+0

सीधे बिंदु पर! आपको मेरा वोट मिला –

0

हाल ही में मैं एक बहुत ही इसी तरह की समस्या का सामना करना आ गया। बस हमारे साथ हमारे समाधान साझा करना चाहता था। मैं चाहता था कि मैं का उपयोग कर जो एक enum था एक json वस्तु से एक Car<CarA> का एक उदाहरण बनाया:

Dictionary<MyEnum, Type> mapper = new Dictionary<MyEnum, Type>(); 

mapper.Add(1, typeof(CarA)); 
mapper.Add(2, typeof(BarB)); 

public class Car<T> where T : class 
{  
    public T Detail { get; set; } 
    public Car(T data) 
    { 
     Detail = data; 
    } 
} 
public class CarA 
{ 
    public int PropA { get; set; } 
    public CarA(){} 
} 
public class CarB 
{ 
    public int PropB { get; set; } 
    public CarB(){} 
} 

var jsonObj = {"Type":"1","PropA":"10"} 
MyEnum t = GetTypeOfCar(jsonObj); 
Type objectT = mapper[t] 
Type genericType = typeof(Car<>); 
Type carTypeWithGenerics = genericType.MakeGenericType(objectT); 
Activator.CreateInstance(carTypeWithGenerics , new Object[] { JsonConvert.DeserializeObject(jsonObj, objectT) }); 
-2

यह अभी भी उच्च प्रदर्शन के साथ संभव है, निम्न कार्य करके:

// 
    public List<R> GetAllItems<R>() where R : IBaseRO, new() { 
     var list = new List<R>(); 
     using (var wl = new ReaderLock<T>(this)) { 
      foreach (var bo in this.items) { 
       T t = bo.Value.Data as T; 
       R r = new R(); 
       r.Initialize(t); 
       list.Add(r); 
      } 
     } 
     return list; 
    } 

और

// 
///<summary>Base class for read-only objects</summary> 
public partial interface IBaseRO { 
    void Initialize(IDTO dto); 
    void Initialize(object value); 
} 

प्रासंगिक कक्षाओं को तब इस इंटरफ़ेस से प्राप्त करना होगा और तदनुसार आरंभ करना होगा। कृपया ध्यान दें, कि मेरे मामले में, यह कोड आसपास के वर्ग का हिस्सा है, जो पहले से ही < टी > सामान्य पैरामीटर के रूप में है। आर, मेरे मामले में, केवल पढ़ने-योग्य वर्ग भी है। आईएमओ, प्रारंभिक() कार्यों की सार्वजनिक उपलब्धता अपरिवर्तनीयता पर कोई नकारात्मक प्रभाव नहीं पड़ता है। इस वर्ग का उपयोगकर्ता एक और वस्तु डाल सकता है, लेकिन यह अंतर्निहित संग्रह को संशोधित नहीं करेगा।

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