सी # में, एक विधि के अंदर एक पारित सामान्य प्रकार को कैसे त्वरित किया जाए?


99

मैं InstantiateType<T>नीचे अपनी विधि के अंदर टाइप T कैसे लिख सकता हूं ?

मुझे त्रुटि मिल रही है: 'T' एक 'टाइप पैरामीटर' है, लेकिन इसका उपयोग 'वेरिएबल' की तरह किया जाता है। :

(स्कैन किए गए उत्तर के लिए स्कैन करें)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestGeneric33
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            Console.WriteLine(container.InstantiateType<Customer>("Jim", "Smith"));
            Console.WriteLine(container.InstantiateType<Employee>("Joe", "Thompson"));
            Console.ReadLine();
        }
    }

    public class Container
    {
        public T InstantiateType<T>(string firstName, string lastName) where T : IPerson
        {
            T obj = T();
            obj.FirstName(firstName);
            obj.LastName(lastName);
            return obj;
        }

    }

    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }

    public class Customer : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
    }

    public class Employee : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int EmployeeNumber { get; set; }
    }
}

सम्मानित किया गया उत्तर:

सभी टिप्पणियों के लिए धन्यवाद, उन्होंने मुझे सही रास्ते पर ले लिया, यह वही है जो मैं करना चाहता था:

using System;

namespace TestGeneric33
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            Customer customer1 = container.InstantiateType<Customer>("Jim", "Smith");
            Employee employee1 = container.InstantiateType<Employee>("Joe", "Thompson");
            Console.WriteLine(PersonDisplayer.SimpleDisplay(customer1));
            Console.WriteLine(PersonDisplayer.SimpleDisplay(employee1));
            Console.ReadLine();
        }
    }

    public class Container
    {
        public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
        {
            T obj = new T();
            obj.FirstName = firstName;
            obj.LastName = lastName;
            return obj;
        }
    }

    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }

    public class PersonDisplayer
    {
        private IPerson _person;

        public PersonDisplayer(IPerson person)
        {
            _person = person;
        }

        public string SimpleDisplay()
        {
            return String.Format("{1}, {0}", _person.FirstName, _person.LastName);
        }

        public static string SimpleDisplay(IPerson person)
        {
            PersonDisplayer personDisplayer = new PersonDisplayer(person);
            return personDisplayer.SimpleDisplay();
        }
    }

    public class Customer : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
    }

    public class Employee : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int EmployeeNumber { get; set; }
    }
}

बेहतर डिज़ाइन पैटर्न में बदलाव के लिए +1।
जोएल कोएहॉर्न

+1 बेहद करीने से टाइप किए गए कोड के लिए, एक दुर्लभ वस्तु।
नवाफल

जवाबों:


131

अपनी विधि इस तरह घोषित करें:

public string InstantiateType<T>(string firstName, string lastName) 
              where T : IPerson, new()

अंत में अतिरिक्त बाधा की सूचना दें। फिर newविधि बॉडी में एक उदाहरण बनाएं :

T obj = new T();    

4
मैं अपने दिनों में कुछ भारी जेनेरिक टाइपिंग के दुरुपयोग के साथ सी # लिख रहा हूं, और मुझे कभी नहीं पता था कि आप इस तरह की बाधा को एक सामान्य प्रकार को तुरंत परिभाषित कर सकते हैं। बहुत धन्यवाद!
निकोलस मार्टेल

बहुत बहुत अच्छा!!
सॉटिरिस ज़ेगनेनिस

क्या होगा यदि कोई निर्दिष्ट प्रकार नहीं है, क्या यह संभव है?
jj

31

तरीकों की जोड़ी।

प्रकार को निर्दिष्ट किए बिना एक निर्माता होना चाहिए:

T obj = default(T); //which will produce null for reference types

एक निर्माता के साथ:

T obj = new T();

लेकिन इसके लिए क्लॉज की आवश्यकता होती है:

where T : new()

1
पहले वाला संदर्भ प्रकारों के लिए एक उदाहरण बनाने के बजाय अशक्त होगा।
जोएल कोएहॉर्न

1
हां। आपको डिफ़ॉल्ट निर्माता के बिना प्रकार बनाने के लिए प्रतिबिंब का उपयोग करने की आवश्यकता है, डिफ़ॉल्ट (टी) सभी संदर्भ प्रकारों के लिए अशक्त है।
दान सी।

1
हां बिल्कुल, वास्तव में पूर्णता के लिए शामिल।
अन्नकूट

13

उपरोक्त उत्तरों पर विस्तार करने के लिए, where T:new()एक सामान्य विधि में बाधा जोड़ने के लिए T को एक सार्वजनिक, पैरामीटर रहित निर्माता की आवश्यकता होगी।

यदि आप उससे बचना चाहते हैं - और फ़ैक्टरी पैटर्न में आप कभी-कभी दूसरों को अपनी फ़ैक्टरी विधि से जाने के लिए बाध्य करते हैं और सीधे निर्माणकर्ता के माध्यम से नहीं - तो फिर विकल्प यह है कि प्रतिबिंब का उपयोग करें ( Activator.CreateInstance...) और डिफ़ॉल्ट कंस्ट्रक्टर को निजी रखें। लेकिन यह निश्चित रूप से एक प्रदर्शन दंड के साथ आता है।


यह पहली बार नहीं है जब लोग "सभी अन्य उत्तर" :)
दान सी।

मैं कभी-कभी घबराहट से 'प्रतिस्पर्धा' के उत्तरों को उखाड़ फेंकने के लिए स्वीकार नहीं करूंगा, जब तक कि शाम को एक सवाल पर सुलझ न जाए: DI का अनुमान (गैर-अंक) कर्म उन्हें सुलझा देगा!
रूबेन बार्टलिंक

8

आप नया T () चाहते हैं , लेकिन आपको फैक्टरी विधि के लिए कल्पना को जोड़ना , new()होगाwhere


मैं इसे वापस टकराया, मैंने इसे समझा, मदद की, यह सामान्य लोगों की तरह लगता है जैसे पोस्ट किए गए कोड यहां वर्णन से बेहतर हैं
एडवर्ड टंगुए

धन्यवाद, दुनिया फिर से समझ में आता है!
रुबेन बार्टेलिंक

सही है, लेकिन आपका जवाब संक्षेप में थोड़ी तरफ है;)
लोरेंज लो सॉयर

4

थोड़ा पुराना है, लेकिन दूसरों के लिए एक समाधान की तलाश में, शायद यह ब्याज की हो सकती है: http://daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-pार्थ-constructors/

दो उपाय। एक का उपयोग कर उत्प्रेरक और एक का उपयोग कर संकलित Lambdas।

//Person has private ctor
var person = Factory<Person>.Create(p => p.Name = "Daniel");

public static class Factory<T> where T : class 
{
    private static readonly Func<T> FactoryFn;

    static Factory()
    {
        //FactoryFn = CreateUsingActivator();

        FactoryFn = CreateUsingLambdas();
    }

    private static Func<T> CreateUsingActivator()
    {
        var type = typeof(T);

        Func<T> f = () => Activator.CreateInstance(type, true) as T;

        return f;
    }

    private static Func<T> CreateUsingLambdas()
    {
        var type = typeof(T);

        var ctor = type.GetConstructor(
            BindingFlags.Instance | BindingFlags.CreateInstance |
            BindingFlags.NonPublic,
            null, new Type[] { }, null);

        var ctorExpression = Expression.New(ctor);
        return Expression.Lambda<Func<T>>(ctorExpression).Compile();
    }

    public static T Create(Action<T> init)
    {
        var instance = FactoryFn();

        init(instance);

        return instance;
    }
}

2

आप ऑब्जेक्ट के कंस्ट्रक्टर को लाने के लिए प्रतिबिंब का उपयोग कर सकते हैं और उस तरह से तुरंत कर सकते हैं:

var c = typeof(T).GetConstructor();
T t = (T)c.Invoke();

1

संकलित लांबा अभिव्यक्ति के साथ अपनी वस्तु का निर्माण करने के लिए एक फैक्ट्री क्लास का उपयोग करना: सबसे तेज़ तरीका मैंने जेनेरिक प्रकार को तत्काल करने के लिए पाया है।

public static class FactoryContructor<T>
{
    private static readonly Func<T> New =
        Expression.Lambda<Func<T>>(Expression.New(typeof (T))).Compile();

    public static T Create()
    {
        return New();
    }
}

यहाँ बेंचमार्क सेट करने के लिए मेरे द्वारा उठाए गए कदम हैं।

मेरी बेंचमार्क टेस्ट विधि बनाएँ:

static void Benchmark(Action action, int iterationCount, string text)
{
    GC.Collect();
    var sw = new Stopwatch();
    action(); // Execute once before

    sw.Start();
    for (var i = 0; i <= iterationCount; i++)
    {
        action();
    }

    sw.Stop();
    System.Console.WriteLine(text + ", Elapsed: {0}ms", sw.ElapsedMilliseconds);
}

मैंने कारखाना विधि का उपयोग करने की भी कोशिश की है:

public static T FactoryMethod<T>() where T : new()
{
    return new T();
}

परीक्षण के लिए मैंने सबसे सरल वर्ग बनाया है:

public class A { }

परीक्षण करने के लिए स्क्रिप्ट:

const int iterations = 1000000;
Benchmark(() => new A(), iterations, "new A()");
Benchmark(() => FactoryMethod<A>(), iterations, "FactoryMethod<A>()");
Benchmark(() => FactoryClass<A>.Create(), iterations, "FactoryClass<A>.Create()");
Benchmark(() => Activator.CreateInstance<A>(), iterations, "Activator.CreateInstance<A>()");
Benchmark(() => Activator.CreateInstance(typeof (A)), iterations, "Activator.CreateInstance(typeof (A))");

1 000 से अधिक पुनरावृत्तियों के परिणाम:

new A (): 11ms

FactoryMethod A (): 275ms

FactoryClass A .Create (): 56ms

एक्टीवेटर। क्रिएटइन्स्टेंस ए (): 235ms

एक्टीवेटर.क्रीट इनस्टेंस (टाइपोफ़ (ए)): 157 मी

टिप्पणी : मैंने .NET फ्रेमवर्क 4.5 और 4.6 (समकक्ष परिणाम) दोनों का उपयोग करके परीक्षण किया है ।


0

इसके बजाय प्रकार बनाने के लिए एक समारोह बनाने के लिए

public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
    {
        T obj = new T();
        obj.FirstName = firstName;
        obj.LastName = lastName;
        return obj;
    }

आप इसे इस तरह से कर सकते थे

T obj = new T { FirstName = firstName, LastName = lastname };

1
यह पूछे जा रहे सवाल का जवाब नहीं देता है। यहां वास्तविक समस्या यह थी कि उसे सामान्य वर्ग का एक नया उदाहरण बनाने की जरूरत थी। शायद यह अनपेक्षित था, लेकिन ऐसा लगता है जैसे आप कह रहे हैं कि शुरुआती का उपयोग करने से मूल समस्या हल हो जाएगी, लेकिन ऐसा नहीं है। new()बाधा अभी भी काम करने के लिए अपने जवाब के लिए सामान्य प्रकार पर की जरूरत है।
यूजर

यदि आप मददगार बनने की कोशिश कर रहे हैं और सुझाव देते हैं कि इनिशियलाइज़र यहाँ एक उपयोगी उपकरण है, तो आपको एक टिप्पणी के रूप में पोस्ट करना चाहिए, दूसरा उत्तर नहीं।
यूजर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.