সি # তে, কোনও পদ্ধতির মধ্যে কীভাবে পাস করা জেনেরিক প্রকারটি ইনস্ট্যান্ট করবেন?


100

আমি কীভাবে InstantiateType<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, একটি বিরলতা।
নওফাল

উত্তর:


133

আপনার পদ্ধতিটি এভাবে ঘোষণা করুন:

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

শেষে অতিরিক্ত বাধা লক্ষ করুন। তারপরে newমেথড বডিটিতে একটি উদাহরণ তৈরি করুন :

T obj = new T();    

4
আমি আমার দিনগুলিতে কিছু ভারী জেনেরিক টাইপিং অপব্যবহারের সাথে কয়েক বছর ধরে সি # লিখছি এবং আমি জানি না যে জেনেরিক ধরণের ইনস্ট্যান্ট করতে আপনি এই জাতীয় সীমাবদ্ধতার সংজ্ঞা দিতে পারবেন। অনেক ধন্যবাদ!
নিকোলাস মার্টেল

খুব খুব চমৎকার!!
সোটিরিস জেজিয়ানিস

যদি নির্দিষ্ট কোন প্রকার নির্দিষ্ট না হয়, তবে তা কি সম্ভব?
জেজে

31

উপায় কয়েক।

প্রকারটি নির্দিষ্ট না করে অবশ্যই কোনও নির্মাণকারী থাকতে হবে:

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

একজন কনস্ট্রাক্টর সহ:

T obj = new T();

তবে এর জন্য এই ধারাটি দরকার:

where T : new()

4
প্রথমটি রেফারেন্স ধরণের জন্য উদাহরণ তৈরি করার পরিবর্তে নাল বরাদ্দ করবে।
জোয়েল কোহর্ন

4
হ্যাঁ আপনাকে কোনও ডিফল্ট কনস্ট্রাক্টর ছাড়াই প্রকার তৈরি করতে প্রতিবিম্বটি ব্যবহার করতে হবে, সমস্ত রেফারেন্স ধরণের জন্য ডিফল্ট (টি) নাল।
ড্যান সি

4
হ্যাঁ একেবারে, সম্পূর্ণতার জন্য অন্তর্ভুক্ত।
অন্নাকাটা

13

উপরের উত্তরগুলিতে প্রসারিত করার জন্য, where T:new()জেনেরিক পদ্ধতিতে সীমাবদ্ধতা যুক্ত করার জন্য টির পাবলিক, প্যারামিটারলেস কনস্ট্রাক্টর লাগবে।

আপনি যদি তা এড়াতে চান - এবং কারখানার প্যাটার্নে আপনি কখনও কখনও অন্যকে আপনার কারখানার পদ্ধতিতে যেতে বাধ্য হন এবং সরাসরি নির্মাতার মাধ্যমে নয় - তবে বিকল্পটি প্রতিফলন ( Activator.CreateInstance...) ব্যবহার করা এবং ডিফল্ট কনস্ট্রাক্টরকে ব্যক্তিগত রাখতে হয়। তবে এটি অবশ্যই পারফরম্যান্স পেনাল্টি নিয়ে আসে।


এটি প্রথমবার নয় যখন লোকেরা "অন্যান্য সমস্ত উত্তর" কে কমিয়ে দেয় :) :)
ড্যান সি

অবিশ্বাস্য প্রশ্নটির সমাধান না হওয়া অবধি আমি কখনও কখনও তীব্রভাবে 'প্রতিযোগিতামূলক' উত্তরগুলি অগ্রাহ্য না করার বিষয়টি স্বীকার করব: ডিআই অনুমান (বিন্দু) কর্মফল সেগুলি সাজিয়ে ফেলবে!
রুবেন বারটেলিংক

8

আপনি নতুন টি () চান, তবে আপনাকে ফ্যাক্টরি পদ্ধতির জন্য আরও কিছু যুক্ত , new()করতে হবেwhere


আমি এটিকে ব্যাক আপ করেছিলাম, আমি এটি বুঝতে পেরেছি, সাহায্য করেছি, এটি সাধারণ বর্ণনায় বর্ণিত পোস্টের কোডের মতো দেখে মনে হয়
এডওয়ার্ড টাঙ্গুয়ে

ধন্যবাদ, পৃথিবী আবারও বোঝা যায়!
রুবেন বারটেলিংক

ঠিক আছে তবে আপনার উত্তরটি স্বল্প দিক থেকে স্বীকার করা হয়েছে;)
লরেঞ্জ লো সৌর

4

কিছুটা পুরানো তবে অন্যদের জন্য সমাধান সন্ধানের জন্য, সম্ভবত এটি আগ্রহী হতে পারে: http://daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/

দুটি সমাধান। একজন অ্যাক্টিভেটর এবং অন্যটি সংকলিত লাম্বডাস ব্যবহার করছেন।

//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 000 পুনরাবৃত্তির উপরে ফলাফল:

নতুন এ (): 11 মিমি

ফ্যাক্টরিমাথোদ এ (): 275 মিমি

ফ্যাক্টরিক্লাস এ। তৈরি করুন (): 56 মিমি

অ্যাক্টিভেটর.ক্রিয়েট ইনস্ট্যান্স এ (): 235 মিমি

অ্যাক্টিভেটর.ক্রিয়েট ইনস্ট্যান্স (টাইফফ (এ)): 157 মিমি

মন্তব্য : আমি। নেট ফ্রেমওয়ার্ক 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 };

4
এটি জিজ্ঞাসা করা প্রশ্নের উত্তর দেয় না। এখানে আসল সমস্যাটি ছিল তাঁর জেনেরিক শ্রেণির একটি নতুন উদাহরণ তৈরি করা দরকার। সম্ভবত এটি অনিচ্ছাকৃত ছিল, তবে মনে হচ্ছে আপনি বলছেন যে ইনিশিয়ালাইজার ব্যবহার করা আসল সমস্যাটি সমাধান করবে তবে তা তা নয়। new()বাধ্যতা এখনও কাজ আপনার উত্তরের জন্য জেনেরিক ধরনের উপর প্রয়োজন হয়।
ব্যবহারকারী

আপনি যদি সহায়ক হতে চেষ্টা করছেন এবং পরামর্শদাতা এখানে একটি দরকারী সরঞ্জাম হিসাবে পরামর্শ দিচ্ছেন, তবে আপনার মন্তব্যটি পোস্ট করা উচিত, অন্য উত্তর নয়।
ব্যবহারকারী
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.