জেনেরিক টাইপের উদাহরণ তৈরি করুন যার নির্মাণকারীর একটি পরামিতি প্রয়োজন?


229

যদি BaseFruitএমন কোনও কনস্ট্রাক্টর থাকে যা একটি গ্রহণ করে int weight, আমি কি জেনেরিক পদ্ধতিতে এই জাতীয় কোনও ফলকে ইনস্ট্যান্ট করতে পারি?

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

মন্তব্যের পিছনে একটি উদাহরণ যুক্ত করা হয়েছে। মনে হচ্ছে আমি কেবল তখনই এটি করতে পারি যদি আমি BaseFruitপ্যারামিটারলেস কনস্ট্রাক্টর দিয়ে থাকি এবং তারপরে সদস্য ভেরিয়েবলের মাধ্যমে সমস্ত কিছু পূরণ করি। আমার আসল কোডে (ফল সম্পর্কে নয়) এটি বরং ব্যবহারিক।

-Update-
সুতরাং দেখে মনে হচ্ছে এটি কোনওভাবেই সীমাবদ্ধতার দ্বারা সমাধান করা যায় না। উত্তরগুলি থেকে তিনটি প্রার্থীর সমাধান রয়েছে:

  • কারখানার প্যাটার্ন
  • প্রতিফলন
  • একটিভেটর

আমি মনে করি প্রতিবিম্ব সবচেয়ে কম পরিষ্কার, তবে আমি অন্য দুটির মধ্যে সিদ্ধান্ত নিতে পারি না।


1
বিটিডাব্লু: আজ আমি সম্ভবত এটির পছন্দের আইওসি লাইব্রেরি দিয়ে সমাধান করব।
বরিস কলেনস

প্রতিবিম্ব এবং অ্যাক্টিভেটর আসলে ঘনিষ্ঠভাবে সম্পর্কিত।
রব ভার্মিউলেন

উত্তর:


335

অতিরিক্ত একটি সহজ উদাহরণ:

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

নোট করুন যে টি তে নতুন () সীমাবদ্ধতা ব্যবহার করা কেবল সংকলনের সময় কোনও পাবলিক প্যারামিটারলেস কনস্ট্রাক্টরের জন্য সংকলক চেক করার জন্য, টাইপটি তৈরি করতে ব্যবহৃত প্রকৃত কোডটি হল অ্যাক্টিভেটর শ্রেণি।

বিদ্যমান নির্দিষ্ট কন্সট্রাক্টর সম্পর্কে আপনাকে নিজেকে নিশ্চিত করতে হবে এবং এই ধরণের প্রয়োজন হতে পারে কোড গন্ধ (বা বরং এমন কিছু যা আপনার কেবলমাত্র সি # তে বর্তমান সংস্করণে এড়াতে চেষ্টা করা উচিত)।


যেহেতু এই কনস্ট্রাক্টরটি বেসক্লাসে রয়েছে (বেসফ্রুট) আমি জানি এটির একটি কনস্ট্রাক্টর থাকবে। তবে প্রকৃতপক্ষে, যদি একদিন আমি স্থির করি বেসফলের আরও বেশি প্যারামিটার দরকার হয় তবে আমার ক্ষতি হতে পারে। যদিও অ্যাক্টিভেটর শ্রেণিতে সন্ধান করবে। এর আগে শুনিনি।
বরিস কলেনস

3
এই এক ভাল কাজ করে। একটি ক্রিয়েটিইনস্ট্যান্স <টি> () পদ্ধতিও রয়েছে, তবে এতে কিছু রসনের পরামিতিগুলির জন্য কোনও ওভারলোড নেই
বোরিস

20
ব্যবহার করার দরকার নেই new object[] { weight }CreateInstanceপ্যারাম দিয়ে ঘোষিত হয় public static object CreateInstance(Type type, params object[] args), তাই আপনি ঠিক করতে পারেন return (T) Activator.CreateInstance(typeof(T), weight);। যদি একাধিক পরামিতি থাকে তবে তাদের পৃথক যুক্তি হিসাবে পাস করুন। শুধু যদি আপনি ইতিমধ্যে পরামিতি একটি গণনীয় নির্মাণ আছে আপনি এটি রূপান্তর করতে বিরক্ত করা উচিত object[]এবং যে পাস CreateInstance
এরিক

2
এটিতে আমি পড়েছি এমন পারফরম্যান্স সংক্রান্ত সমস্যা থাকবে। পরিবর্তে একটি সংকলিত লাম্বদা ব্যবহার করুন। vagifabilov.wordpress.com/2010/04/02/…
ডেভিড

1
@ রোবর্মিউলেন - আমি প্রতিটি ফলের শ্রেণিতে স্থির সম্পত্তি হিসাবে এমন কিছু মনে করি যাতে এতে Funcনতুন উদাহরণ তৈরি হয়। ধরুন Appleকনস্ট্রাক্টর ব্যবহার হ'ল new Apple(wgt)। তারপর যোগ Appleবর্গ এই সংজ্ঞা: static Func<float, Fruit> CreateOne { get; } = (wgt) => new Apple(wgt);কারখানা সংজ্ঞায়িত public static Fruit CreateFruitGiven(float weight, Func<float, Fruit> createOne) { return createOne(weight); } ব্যবহার: Factory.CreateFruit(57.3f, Apple.CreateOne);- যে তৈরি করে এবং একটি ফেরৎ Appleসঙ্গে, weight=57.3f
টুলমেকারস্টেভ

92

আপনি কোনও প্যারামিটারাইজড কনস্ট্রাক্টর ব্যবহার করতে পারবেন না। আপনার যদি কোনও where T : new()বাধা থাকে তবে আপনি প্যারামিটারলেস কনস্ট্রাক্টর ব্যবহার করতে পারেন ।

এটি একটি বেদনা, তবে এটাই জীবন :(

এটি "স্ট্যাটিক ইন্টারফেস" দিয়ে আমি যে বিষয়গুলি সম্বোধন করতে চাই তার মধ্যে একটি এটি । আপনি তখন স্থির পদ্ধতি, অপারেটর এবং নির্মাণকারীদের অন্তর্ভুক্ত করতে টি-কে সীমাবদ্ধ করতে সক্ষম হবেন এবং তারপরে তাদের কল করতে পারবেন।


2
কমপক্ষে আপনি এ জাতীয় প্রতিবন্ধকতাগুলি করতে পারেন - জাভা সর্বদা আমাকে হতাশ করে।
মার্সেল জ্যাকওয়ার্থ

@ জোনস্কিট: আমি যদি ভিবি 6.0 তে ডেকে আনার জন্য নেট নেট জেনেরিকের সাথে API টি উন্মুক্ত করে রেখেছি ... এটি এখনও কার্যকর হয় না?
রায় লি

@ রোলি: আমার কোনও ধারণা নেই, তবে আমি সন্দেহ করি না।
জন স্কিটি

আমি মনে করি রানটাইম পরিবর্তন না করে কোনও ভাষা সংকলক দ্বারা স্থির ইন্টারফেস যুক্ত করা যেতে পারে, যদিও ভাষা দলগুলির বিবরণে সমন্বয় করা ভাল হবে। নির্দিষ্ট করুন যে স্ট্যাটিক ইন্টারফেস বাস্তবায়নের দাবি করা প্রতিটি শ্রেণীর একটি নির্দিষ্ট ইন্টারফেস-সম্পর্কিত নাম সহ একটি নেস্টেড বর্গ থাকতে হবে, যা তার নিজস্ব ধরণের একটি স্ট্যাটিক সিঙ্গলটন উদাহরণকে সংজ্ঞায়িত করে। ইন্টারফেসের সাথে যুক্ত হ'ল একটি স্ট্যাটিক জেনেরিক টাইপ হবে একটি উদাহরণ ক্ষেত্রের সাথে যা একবার প্রতিবিম্বের মাধ্যমে সিঙ্গলটনের সাথে লোড করা প্রয়োজন, তবে এর পরে সরাসরি ব্যবহার করা যেতে পারে।
সুপারক্যাট

একটি প্যারামিটারাইজড কনস্ট্রাক্টর সীমাবদ্ধতা অনেক একইভাবে পরিচালনা করা যেতে পারে (একটি ফ্যাক্টরি পদ্ধতি ব্যবহার করে এবং তার ফেরতের ধরণের জন্য জেনেরিক প্যারামিটার); উভয় ক্ষেত্রেই কোনও ভাষায় লিখিত কোড কোনওরকম প্রতিরোধ করবে না যা যথাযথ স্ট্যাটিক প্রকারটি সংজ্ঞায়িত না করে ইন্টারফেস প্রয়োগের দাবি করার পক্ষে এই বৈশিষ্ট্যটির সমর্থন করে না, সুতরাং এই জাতীয় ভাষা ব্যবহার করে লিখিত কোড রানটাইম এ ব্যর্থ হতে পারে, তবে প্রতিচ্ছবি ব্যবহারকারীর মধ্যে এড়ানো যেতে পারে কোড।
সুপারক্যাট

61

হ্যাঁ; কোথায় হবে আপনার পরিবর্তন করুন:

where T:BaseFruit, new()

তবে এটি কেবল প্যারামিটারলেস কনস্ট্রাক্টরের সাথে কাজ করে । আপনার নিজের সম্পত্তি সেট করার কিছু অন্য উপায় থাকতে হবে (সম্পত্তি নিজেই সেট করতে হবে বা এই জাতীয় কিছু)।


যদি কনস্ট্রাক্টরের প্যারামিটার না থাকে তবে এটি আমার কাছে নিরাপদ বলে মনে হচ্ছে।
PerpetualStudent

আপনি আমার জীবন বাঁচিয়েছেন। আমি টি ক্লাস এবং নতুন () কীওয়ার্ডে সীমাবদ্ধ করতে পারিনি।
জেনোটাইপেক

28

সর্বাধিক সহজ সমাধান Activator.CreateInstance<T>()


1
পরামর্শের জন্য ধন্যবাদ, আমার যেখানে থাকার দরকার ছিল তা আমাকে পেয়েছে। যদিও এটি আপনাকে প্যারামিটারাইজড কনস্ট্রাক্টর ব্যবহার করতে দেয় না। তবে আপনি অ-জেনেরিক ভেরিয়েন্টটি ব্যবহার করতে পারেন: অ্যাক্টিভেটর.ক্রেইটআইনস্ট্যান্স (টাইপফ (টি), নতুন অবজেক্ট [] {...}) যেখানে অবজেক্ট অ্যারেটিতে কনস্ট্রাক্টরের আর্গুমেন্ট রয়েছে।
রব ভার্মিউলেন

19

জোন যেহেতু ইঙ্গিত করেছেন এটি একটি পরামিতিবিহীন নির্মাণকারীকে সীমাবদ্ধ করার জন্য জীবন। তবে একটি আলাদা সমাধান হ'ল ফ্যাক্টরি প্যাটার্ন ব্যবহার করা। এটি সহজেই সীমাবদ্ধ

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
ভাল পরামর্শ - যদিও আপনি যত্নবান না হন তবে আপনি জাভা DOM এপিআই-এর নরকে শেষ করতে পারেন, কারখানার গৌরব সহ :(
জোন স্কিট

হ্যাঁ, এটি এমন একটি সমাধান যা আমি নিজেকে বিবেচনা করছি। তবে আমি সীমাবদ্ধতার লাইনে কিছু আশা করছিলাম। তখন অনুমান করবেন না ..
বরিস কলেনস

@ বোরিস, দুর্ভাগ্যক্রমে আপনি যে সীমাবদ্ধ ভাষার সন্ধান করছেন তা এই সময়ে উপস্থিত নেই
জারেডপাড়

11

আপনি প্রতিবিম্ব ব্যবহার করে করতে পারেন:

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) });
}

যদিও আমি প্রতিবিম্বের ওভারহেড পছন্দ করি না, যেমন অন্যেরা ব্যাখ্যা করেছেন, বর্তমানে ঠিক এটিই এটি। এই নির্মাতাকে কীভাবে খুব বেশি ডাকা হবে না তা দেখে আমি এই সাথে যেতে পারি। বা কারখানা। এখনও জানিনা।
বরিস কলেনস

এটি বর্তমানে আমার পছন্দের পদ্ধতির কারণ এটি আমন্ত্রণের দিকে আরও জটিলতা যুক্ত করে না।
রব ভার্মুলেন

তবে এখন আমি অ্যাক্টিভেটর পরামর্শটি পড়েছি, যা উপরের প্রতিফলন সমাধানের মতোই ঘৃণ্যতা রয়েছে, তবে কোডের কম লাইন সহ :) আমি অ্যাক্টিভেটর বিকল্পের জন্য যাচ্ছি।
রব ভার্মুলেন

1

ব্যবহারকারী 1471935 এর পরামর্শে যুক্ত হিসাবে:

এক বা একাধিক পরামিতি সহ কনস্ট্রাক্টর ব্যবহার করে জেনেরিক ক্লাস ইনস্ট্যান্ট করতে আপনি এখন অ্যাক্টিভেটর ক্লাস ব্যবহার করতে পারেন।

T instance = Activator.CreateInstance(typeof(T), new object[] {...}) 

অবজেক্টের তালিকাটি আপনি সরবরাহ করতে চান এমন প্যারামিটার। মাইক্রোসফ্ট অনুসারে :

ক্রিয়েটইনস্ট্যান্স [...] নির্ধারক ব্যবহার করে নির্দিষ্ট ধরণের একটি উদাহরণ তৈরি করে যা নির্দিষ্ট পরামিতিগুলির সাথে সর্বোত্তমভাবে মেলে।

ক্রিয়েটইনস্টান্স ( CreateInstance<T>()) এর জেনেরিক সংস্করণও রয়েছে তবে এটি আপনাকে কনস্ট্রাক্টর পরামিতি সরবরাহ করতে দেয় না।


1

আমি এই পদ্ধতিটি তৈরি করেছি:

public static V ConvertParentObjToChildObj<T,V> (T obj) where V : new()
{
    Type typeT = typeof(T);
    PropertyInfo[] propertiesT = typeT.GetProperties();
    V newV = new V();
    foreach (var propT in propertiesT)
    {
        var nomePropT = propT.Name;
        var valuePropT = propT.GetValue(obj, null);

        Type typeV = typeof(V);
        PropertyInfo[] propertiesV = typeV.GetProperties();
        foreach (var propV in propertiesV)
        {
            var nomePropV = propV.Name;
            if(nomePropT == nomePropV)
            {
                propV.SetValue(newV, valuePropT);
                break;
            }
        }
    }
    return newV;
}

আমি এটি এইভাবে ব্যবহার করি:

public class A 
{
    public int PROP1 {get; set;}
}

public class B : A
{
    public int PROP2 {get; set;}
}

কোড:

A instanceA = new A();
instanceA.PROP1 = 1;

B instanceB = new B();
instanceB = ConvertParentObjToChildObj<A,B>(instanceA);

0

সম্প্রতি আমি একটি খুব অনুরূপ সমস্যা জুড়ে এসেছি। আমাদের সমাধানটি আপনার সবার সাথে ভাগ করে নিতে চেয়েছি। আমি চেয়েছিলাম আমি 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 );
}

এরপরে প্রাসঙ্গিক ক্লাসগুলিকে এই ইন্টারফেস থেকে উদ্ভূত করতে হবে এবং সেই অনুযায়ী সূচনা করতে হবে। দয়া করে মনে রাখবেন, আমার ক্ষেত্রে, এই কোডটি একটি পার্শ্ববর্তী শ্রেণীর অংশ, যা ইতিমধ্যে জেনেরিক প্যারামিটার হিসাবে <T> রয়েছে। আর, আমার ক্ষেত্রে এটি কেবল পঠনযোগ্য শ্রেণি। আইএমও, ইনিশিয়াল () ফাংশনগুলির সর্বজনীন প্রাপ্যতা অপরিবর্তনীয়তার উপর কোনও নেতিবাচক প্রভাব ফেলবে না। এই শ্রেণীর ব্যবহারকারীর মধ্যে অন্য একটি জিনিস রাখা যেতে পারে, তবে এটি অন্তর্নিহিত সংগ্রহটি পরিবর্তন করতে পারে না।

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