আমি কীভাবে সি # জেনেরিক ধরণের সীমাবদ্ধতা হিসাবে ইন্টারফেসটি ব্যবহার করতে পারি?


164

নিম্নলিখিত ফাংশন ঘোষণার উপায় আছে?

public bool Foo<T>() where T : interface;

অর্থাত। যেখানে টি হ'ল একটি ইন্টারফেস প্রকার (অনুরূপ where T : class, এবং struct)।

বর্তমানে আমি এর জন্য নিষ্পত্তি করেছি:

public bool Foo<T>() where T : IBase;

আইবেজকে খালি ইন্টারফেস হিসাবে সংজ্ঞায়িত করা হয়েছে যা আমার সমস্ত কাস্টম ইন্টারফেস দ্বারা উত্তরাধিকার সূত্রে প্রাপ্ত ... আদর্শ নয়, তবে এটির কাজ করা উচিত ... আপনি কেন সংজ্ঞা দিতে পারবেন না যে জেনেরিক টাইপটি একটি ইন্টারফেস হতে হবে?

এটির মূল্যের জন্য, আমি এটি চাই কারণ Fooপ্রতিচ্ছবিটি করা হচ্ছে যেখানে এটির জন্য একটি ইন্টারফেস ধরণের প্রয়োজন ... আমি এটি একটি সাধারণ প্যারামিটার হিসাবে পাস করতে পারি এবং নিজেই ফাংশনটিতে প্রয়োজনীয় চেকিং করতে পারি, তবে এটি অনেক বেশি টাইপসেফ বলে মনে হয়েছিল (এবং আমি ধরুন, আরও কিছু পারফরম্যান্ট, যেহেতু সমস্ত চেক সংকলনের সময় করা হয়েছে)।


4
প্রকৃতপক্ষে, আপনার আইবেস ডিই আমি এখনও অবধি দেখেছি। দুর্ভাগ্যক্রমে, আপনি নিজের মালিকানাধীন ইন্টারফেসের জন্য এটি ব্যবহার করতে পারবেন না। সমস্ত সি # করণীয় হ'ল আইওজেবেক্টের সমস্ত ইন্টারফেস হ'ল উত্তরাধিকার সূত্রে যেমন সমস্ত শ্রেণীর অবজেক্ট থেকে উত্তরাধিকার সূত্রে প্রাপ্ত।
ছড়া

1
দ্রষ্টব্য: এটি একটি বরং সাধারণ ধারণা হতে পারে। খালি ইন্টারফেসের মতো IBase- এইভাবে ব্যবহৃত হয় - তাকে মার্কার ইন্টারফেস বলে । তারা 'চিহ্নিত' ধরণের জন্য বিশেষ আচরণ সক্ষম করে।
পিয়াস

উত্তর:


132

আপনি করতে পারেন সবচেয়ে কাছের (আপনার বেস-ইন্টারফেস পদ্ধতির ব্যতীত) " where T : class" অর্থ রেফারেন্স-টাইপ। "কোনও ইন্টারফেস" বলতে কোনও সিনট্যাক্স নেই।

এটি (" where T : class") ক্লায়েন্টদের পরিষেবা চুক্তিতে (ইন্টারফেস) সীমাবদ্ধ করতে উদাহরণস্বরূপ, WCF- এ ব্যবহৃত হয়।


7
উত্তম উত্তর, তবে এই সিনট্যাক্সটির অস্তিত্ব নেই কেন আপনার কি কোনও ধারণা আছে? দেখে মনে হচ্ছে এটি দুর্দান্ত-সুন্দর বৈশিষ্ট্য হবে।
স্টিফেন হল্ট

@ স্টেফেনহোল্ট: আমার মনে হয় নেট নেট এর নির্মাতারা কোনটি কীভাবে অনুমতি দেবেন তা নির্ধারণ করার ক্ষেত্রে জেনেরিক শ্রেণি এবং পদ্ধতিগুলিকে জেনেরিক প্রকারের সাথে এমন কিছু করতে দেয় যা তারা অন্যথায় ব্যবহার করতে বাধা না দিয়ে পারে না অযৌক্তিক উপায়। বলা হয়ে থাকে যে, কোনও interfaceসীমাবদ্ধতার ক্ষেত্রে Tরেফারেন্স Tএবং অন্য যে কোনও রেফারেন্স ধরণের মধ্যে তুলনা করার অনুমতি দেওয়া উচিত , যেহেতু রেফারেন্স তুলনাগুলি কোনও ইন্টারফেস এবং প্রায় কোনও অন্য রেফারেন্স ধরণের মধ্যে অনুমোদিত এবং এমনকি সেই ক্ষেত্রে তুলনাকেও কোনও সমস্যা হতে পারে না।
সুপারক্যাট

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

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

113

আমি জানি এটি কিছুটা দেরিতে তবে যারা আগ্রহী তাদের জন্য আপনি রানটাইম চেক ব্যবহার করতে পারেন।

typeof(T).IsInterface

11
এটি উল্লেখ করার একমাত্র উত্তর হওয়ার জন্য +1। আমি কার্যপ্রণালী উন্নয়নের জন্য একটি পদ্ধতির সাথে একটি উত্তর যুক্ত করেছি প্রতি বার পদ্ধতিটি প্রতিবার পরিবর্তে একবার বলার চেয়ে একবারে পরীক্ষা করে।
ফগ

9
সি # তে জেনারিকের সম্পূর্ণ ধারণাটি হ'ল সংকলন-সময় সুরক্ষা। আপনি যা পরামর্শ দিচ্ছেন তা অ-জেনেরিক পদ্ধতিতেও সম্পাদন করা যেতে পারে Foo(Type type)
জেসেক গর্গোń 20

আমি রানটাইম চেক পছন্দ করি। ধন্যবাদ।
তারেক অ্যাজগান গোনার

এছাড়াও রান সময়ে আপনি if (new T() is IMyInterface) { }টি ক্লাস দ্বারা কোনও ইন্টারফেস প্রয়োগ করা হয়েছে কিনা তা পরীক্ষা করতে ব্যবহার করতে পারেন । সবচেয়ে দক্ষ নাও হতে পারে তবে এটি কাজ করে।
টেকারউড

26

না, আসলে আপনি যদি ভাবছেন classএবং এস এবং এস structবোঝাতে চান তবে আপনি ভুল। এর অর্থ কোনও রেফারেন্স টাইপ (যেমন ইন্টারফেসও অন্তর্ভুক্ত) এবং এর অর্থ কোনও মান প্রকার (যেমন , )।classstructclassstructstructenum


1
কোনও শ্রেণি এবং কাঠামোর মধ্যে পার্থক্যের সংজ্ঞা কি এটি নয়: যদিও প্রতিটি শ্রেণি একটি রেফারেন্স টাইপ (এবং বিপরীতে) এবং স্ট্যাক্ট / মান প্রকারের জন্য ডিটো
ম্যাথু শার্লে

ম্যাথু: সি স্ট্রাক্টের চেয়ে আরও বেশি ধরণের মান রয়েছে। উদাহরণস্বরূপ এনামগুলি মান ধরণের এবং ম্যাচের where T : structসীমাবদ্ধতা।
মেহরদাদ আফশারি

এটি লক্ষণীয় যে প্রতিবন্ধকতাগুলিতে ব্যবহৃত একটি ইন্টারফেস প্রকারগুলি বোঝায় না class, তবে একটি ইন্টারফেসের ধরণের স্টোরেজ অবস্থান ঘোষণা করে স্টোরেজ অবস্থানটিকে শ্রেণিক রেফারেন্স হিসাবে ঘোষণা করে যা এই ধরণের প্রয়োগ করে।
সুপারক্যাট

4
আরও সুনির্দিষ্ট হওয়ার জন্য, এর where T : structসাথে মিলে যায় NotNullableValueTypeConstraint, সুতরাং এর অর্থ এটির চেয়ে অন্য কোনও মান ধরণের হতে হবে Nullable<>। (একইভাবে Nullable<>একটি কাঠামোর ধরণ যা where T : structসীমাবদ্ধতা
মেটায় না

19

রবার্টের উত্তর অনুসরণ করতে, এটি আরও পরে, তবে কেবলমাত্র প্রতি টাইপ একবার রানটাইম চেক করতে আপনি একটি স্ট্যাটিক সহায়ক শ্রেণি ব্যবহার করতে পারেন:

public bool Foo<T>() where T : class
{
    FooHelper<T>.Foo();
}

private static class FooHelper<TInterface> where TInterface : class
{
    static FooHelper()
    {
        if (!typeof(TInterface).IsInterface)
            throw // ... some exception
    }
    public static void Foo() { /*...*/ }
}

আমি এটিও নোট করি যে আপনার "কাজ করা উচিত" সমাধান বাস্তবে কাজ করে না। বিবেচনা:

public bool Foo<T>() where T : IBase;
public interface IBase { }
public interface IActual : IBase { string S { get; } }
public class Actual : IActual { public string S { get; set; } }

এখন আপনাকে এভাবে ফু বলা থেকে বিরত করার কিছুই নেই:

Foo<Actual>();

Actualবর্গ, সব সন্তুষ্ট পর IBaseবাধ্যতা।


একটি staticকন্সট্রাকটর হতে পারে না public, তাই এটি একটি কম্পাইল-টাইম এরর দিতে হবে। এছাড়াও আপনার staticক্লাসে একটি উদাহরণ পদ্ধতি রয়েছে এটি একটি সংকলন-সময় ত্রুটি।
জেপ্পে স্টিগ নীলসেন

@ জেপ্পস্টিগনিয়েলসন দ্বারা উল্লিখিত ত্রুটিগুলি সংশোধন করার জন্য নওফালকে ধন্যবাদ জানানো
ফুগ

10

কিছু সময়ের জন্য আমি প্রায়-সংকলন-সময় সীমাবদ্ধতাগুলি নিয়ে ভাবছিলাম, সুতরাং ধারণাটি চালু করার জন্য এটি একটি উপযুক্ত সুযোগ।

মূল ধারণাটি হ'ল আপনি যদি চেক সংকলনের সময়টি না করতে পারেন তবে আপনার উচিত সময়মতো তাড়াতাড়ি সম্ভব এটি করা উচিত যা মূলত অ্যাপ্লিকেশন শুরু হওয়ার মুহুর্তেই। সমস্ত চেক ঠিক থাকলে, অ্যাপ্লিকেশনটি চলবে; যদি কোনও চেক ব্যর্থ হয় তবে আবেদনটি তাত্ক্ষণিকভাবে ব্যর্থ হবে।

আচরণ

সর্বাধিক সম্ভাব্য ফলাফলটি হ'ল সীমাবদ্ধতাগুলি পূরণ না করা হলে আমাদের প্রোগ্রামটি সংকলন করে না। দুর্ভাগ্যক্রমে বর্তমান সি # বাস্তবায়নে এটি সম্ভব নয়।

পরবর্তী সর্বোত্তম জিনিসটি হল প্রোগ্রামটি শুরু হওয়ার মুহুর্তে ক্র্যাশ হয়।

শেষ বিকল্পটি হ'ল কোডটি আঘাতের সাথে সাথে প্রোগ্রামটি ক্র্যাশ করবে। এটি নেট। এর ডিফল্ট আচরণ। আমার জন্য এটি সম্পূর্ণ অগ্রহণযোগ্য।

পূর্বশর্ত

আমাদের আরও একটি বাধা ব্যবস্থা থাকা দরকার, যাতে আরও ভাল কিছু না হওয়ার জন্য ... আসুন একটি বৈশিষ্ট্য ব্যবহার করা যাক। বৈশিষ্ট্যটি আমাদের শর্তগুলির সাথে মেলে কিনা তা যাচাই করার জন্য একটি জেনেরিক সীমাবদ্ধতার শীর্ষে উপস্থিত থাকবে। যদি তা না হয় তবে আমরা একটি কুৎসিত ত্রুটি দিই।

এটি আমাদের কোডে এই জাতীয় জিনিসগুলি করতে সক্ষম করে:

public class Clas<[IsInterface] T> where T : class

(আমি রেখেছি where T:class এখানে , কারণ আমি সর্বদা রানটাইম চেকগুলির তুলনায় কম্পাইল-টাইম চেকগুলি পছন্দ করি)

সুতরাং, এটি কেবল আমাদের 1 টি সমস্যা রেখে দেয়, যা আমরা ব্যবহার করি এমন সমস্ত ধরণের সীমাবদ্ধতার সাথে মেলে কিনা তা যাচাই করে। এটা কতটা কঠিন হতে পারে?

আসুন এটি ভেঙে দিন

জেনেরিক প্রকারগুলি সর্বদা হয় শ্রেণিতে (/ স্ট্রাক্ট / ইন্টারফেস) বা কোনও পদ্ধতিতে থাকে।

সীমাবদ্ধতা ট্রিগার করার জন্য আপনাকে নিম্নলিখিত বিষয়গুলির একটি করতে হবে:

  1. সংকলন-সময়, কোনও প্রকারের টাইপ ব্যবহার করার সময় (উত্তরাধিকার, জেনেরিক সীমাবদ্ধতা, শ্রেণীর সদস্য)
  2. সংকলন-সময়, যখন কোনও পদ্ধতির শরীরে কোনও প্রকার ব্যবহার করা হয়
  3. জেনেরিক বেস শ্রেণির উপর ভিত্তি করে কিছু নির্মাণের জন্য প্রতিচ্ছবি ব্যবহার করার সময় রান-টাইম।
  4. রানটাইম, যখন আরটিটিআইয়ের উপর ভিত্তি করে কিছু নির্মাণের জন্য প্রতিবিম্ব ব্যবহার করে।

এই মুহুর্তে, আমি বলতে চাই যে আপনার যে কোনও প্রোগ্রাম আইএমওতে সর্বদা (4) করা এড়ানো উচিত। নির্বিশেষে, এই চেকগুলি এটি সমর্থন করবে না, কারণ এটি কার্যকরভাবে থামানো সমস্যা সমাধানের অর্থ would

কেস 1: একটি টাইপ ব্যবহার করে

উদাহরণ:

public class TestClass : SomeClass<IMyInterface> { ... } 

উদাহরণ 2:

public class TestClass 
{ 
    SomeClass<IMyInterface> myMember; // or a property, method, etc.
} 

মূলত এর মধ্যে সমস্ত প্রকার, উত্তরাধিকার, সদস্য, পরামিতি, ইত্যাদি ইত্যাদি স্ক্যান করা জড়িত; যদি এটি অ্যারে হয় তবে আমরা উপাদানটির প্রকারটি পরীক্ষা করি।

এই মুহুর্তে আমাকে অবশ্যই যুক্ত করতে হবে যে এটি ডিফল্টরূপে। NET লোড টাইপ করে 'অলস' break সমস্ত ধরণের স্ক্যান করে, আমরা .NET রানটাইমটিকে সমস্ত লোড করতে বাধ্য করি। বেশিরভাগ প্রোগ্রামের জন্য এটি কোনও সমস্যা হওয়া উচিত নয়; তবুও, আপনি যদি নিজের কোডটিতে স্ট্যাটিক ইনিশিয়ালাইজার ব্যবহার করেন তবে আপনার এই পদ্ধতির সাথে সমস্যা দেখা দিতে পারে ... এটি বলেছে, আমি কাউকে যাইহোক এটি করার পরামর্শ দেব না (এই :-) এর মতো বিষয়গুলি বাদ দিয়ে, তাই এটি দেওয়া উচিত নয়) আপনি অনেক সমস্যা।

কেস 2: একটি পদ্ধতিতে একটি ধরণ ব্যবহার করে

উদাহরণ:

void Test() {
    new SomeClass<ISomeInterface>();
}

এটি পরীক্ষা করার জন্য আমাদের কাছে কেবলমাত্র 1 টি বিকল্প রয়েছে: ক্লাসটি ছড়িয়ে ফেলুন, ব্যবহৃত সমস্ত সদস্য টোকেনগুলি পরীক্ষা করুন এবং যদি তাদের মধ্যে একটি জেনেরিক ধরণের হয় - আর্গুমেন্টগুলি পরীক্ষা করুন।

কেস 3: প্রতিবিম্ব, রানটাইম জেনেরিক নির্মাণ

উদাহরণ:

typeof(CtorTest<>).MakeGenericType(typeof(IMyInterface))

আমি মনে করি তাত্ত্বিকভাবে এটি কেস (2) হিসাবে অনুরূপ কৌশল দ্বারা পরীক্ষা করা সম্ভব তবে এটির বাস্তবায়ন অনেক কঠিন (আপনাকে MakeGenericTypeকোনও কোডের পথে ডাকা হয়েছে কিনা তা খতিয়ে দেখা দরকার)। আমি এখানে বিশদে যাব না ...

কেস ৪: প্রতিবিম্ব, রানটাইম আরটিটিআই

উদাহরণ:

Type t = Type.GetType("CtorTest`1[IMyInterface]");

এটি সবচেয়ে খারাপ পরিস্থিতি এবং আমি সাধারণত একটি খারাপ ধারণা আইএমএইচওর আগে ব্যাখ্যা করেছি। যে কোনও উপায়ে, চেকগুলি ব্যবহার করে এটি বের করার কোনও ব্যবহারিক উপায় নেই।

প্রচুর পরীক্ষা হচ্ছে

কেস (1) এবং (2) পরীক্ষা করে এমন একটি প্রোগ্রাম তৈরি করার ফলে এরকম কিছু হবে:

[AttributeUsage(AttributeTargets.GenericParameter)]
public class IsInterface : ConstraintAttribute
{
    public override bool Check(Type genericType)
    {
        return genericType.IsInterface;
    }

    public override string ToString()
    {
        return "Generic type is not an interface";
    }
}

public abstract class ConstraintAttribute : Attribute
{
    public ConstraintAttribute() {}

    public abstract bool Check(Type generic);
}

internal class BigEndianByteReader
{
    public BigEndianByteReader(byte[] data)
    {
        this.data = data;
        this.position = 0;
    }

    private byte[] data;
    private int position;

    public int Position
    {
        get { return position; }
    }

    public bool Eof
    {
        get { return position >= data.Length; }
    }

    public sbyte ReadSByte()
    {
        return (sbyte)data[position++];
    }

    public byte ReadByte()
    {
        return (byte)data[position++];
    }

    public int ReadInt16()
    {
        return ((data[position++] | (data[position++] << 8)));
    }

    public ushort ReadUInt16()
    {
        return (ushort)((data[position++] | (data[position++] << 8)));
    }

    public int ReadInt32()
    {
        return (((data[position++] | (data[position++] << 8)) | (data[position++] << 0x10)) | (data[position++] << 0x18));
    }

    public ulong ReadInt64()
    {
        return (ulong)(((data[position++] | (data[position++] << 8)) | (data[position++] << 0x10)) | (data[position++] << 0x18) | 
                        (data[position++] << 0x20) | (data[position++] << 0x28) | (data[position++] << 0x30) | (data[position++] << 0x38));
    }

    public double ReadDouble()
    {
        var result = BitConverter.ToDouble(data, position);
        position += 8;
        return result;
    }

    public float ReadSingle()
    {
        var result = BitConverter.ToSingle(data, position);
        position += 4;
        return result;
    }
}

internal class ILDecompiler
{
    static ILDecompiler()
    {
        // Initialize our cheat tables
        singleByteOpcodes = new OpCode[0x100];
        multiByteOpcodes = new OpCode[0x100];

        FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
        for (int num1 = 0; num1 < infoArray1.Length; num1++)
        {
            FieldInfo info1 = infoArray1[num1];
            if (info1.FieldType == typeof(OpCode))
            {
                OpCode code1 = (OpCode)info1.GetValue(null);
                ushort num2 = (ushort)code1.Value;
                if (num2 < 0x100)
                {
                    singleByteOpcodes[(int)num2] = code1;
                }
                else
                {
                    if ((num2 & 0xff00) != 0xfe00)
                    {
                        throw new Exception("Invalid opcode: " + num2.ToString());
                    }
                    multiByteOpcodes[num2 & 0xff] = code1;
                }
            }
        }
    }

    private ILDecompiler() { }

    private static OpCode[] singleByteOpcodes;
    private static OpCode[] multiByteOpcodes;

    public static IEnumerable<ILInstruction> Decompile(MethodBase mi, byte[] ildata)
    {
        Module module = mi.Module;

        BigEndianByteReader reader = new BigEndianByteReader(ildata);
        while (!reader.Eof)
        {
            OpCode code = OpCodes.Nop;

            int offset = reader.Position;
            ushort b = reader.ReadByte();
            if (b != 0xfe)
            {
                code = singleByteOpcodes[b];
            }
            else
            {
                b = reader.ReadByte();
                code = multiByteOpcodes[b];
                b |= (ushort)(0xfe00);
            }

            object operand = null;
            switch (code.OperandType)
            {
                case OperandType.InlineBrTarget:
                    operand = reader.ReadInt32() + reader.Position;
                    break;
                case OperandType.InlineField:
                    if (mi is ConstructorInfo)
                    {
                        operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
                    }
                    else
                    {
                        operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                    }
                    break;
                case OperandType.InlineI:
                    operand = reader.ReadInt32();
                    break;
                case OperandType.InlineI8:
                    operand = reader.ReadInt64();
                    break;
                case OperandType.InlineMethod:
                    try
                    {
                        if (mi is ConstructorInfo)
                        {
                            operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
                        }
                        else
                        {
                            operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                        }
                    }
                    catch
                    {
                        operand = null;
                    }
                    break;
                case OperandType.InlineNone:
                    break;
                case OperandType.InlineR:
                    operand = reader.ReadDouble();
                    break;
                case OperandType.InlineSig:
                    operand = module.ResolveSignature(reader.ReadInt32());
                    break;
                case OperandType.InlineString:
                    operand = module.ResolveString(reader.ReadInt32());
                    break;
                case OperandType.InlineSwitch:
                    int count = reader.ReadInt32();
                    int[] targetOffsets = new int[count];
                    for (int i = 0; i < count; ++i)
                    {
                        targetOffsets[i] = reader.ReadInt32();
                    }
                    int pos = reader.Position;
                    for (int i = 0; i < count; ++i)
                    {
                        targetOffsets[i] += pos;
                    }
                    operand = targetOffsets;
                    break;
                case OperandType.InlineTok:
                case OperandType.InlineType:
                    try
                    {
                        if (mi is ConstructorInfo)
                        {
                            operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
                        }
                        else
                        {
                            operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                        }
                    }
                    catch
                    {
                        operand = null;
                    }
                    break;
                case OperandType.InlineVar:
                    operand = reader.ReadUInt16();
                    break;
                case OperandType.ShortInlineBrTarget:
                    operand = reader.ReadSByte() + reader.Position;
                    break;
                case OperandType.ShortInlineI:
                    operand = reader.ReadSByte();
                    break;
                case OperandType.ShortInlineR:
                    operand = reader.ReadSingle();
                    break;
                case OperandType.ShortInlineVar:
                    operand = reader.ReadByte();
                    break;

                default:
                    throw new Exception("Unknown instruction operand; cannot continue. Operand type: " + code.OperandType);
            }

            yield return new ILInstruction(offset, code, operand);
        }
    }
}

public class ILInstruction
{
    public ILInstruction(int offset, OpCode code, object operand)
    {
        this.Offset = offset;
        this.Code = code;
        this.Operand = operand;
    }

    public int Offset { get; private set; }
    public OpCode Code { get; private set; }
    public object Operand { get; private set; }
}

public class IncorrectConstraintException : Exception
{
    public IncorrectConstraintException(string msg, params object[] arg) : base(string.Format(msg, arg)) { }
}

public class ConstraintFailedException : Exception
{
    public ConstraintFailedException(string msg) : base(msg) { }
    public ConstraintFailedException(string msg, params object[] arg) : base(string.Format(msg, arg)) { }
}

public class NCTChecks
{
    public NCTChecks(Type startpoint)
        : this(startpoint.Assembly)
    { }

    public NCTChecks(params Assembly[] ass)
    {
        foreach (var assembly in ass)
        {
            assemblies.Add(assembly);

            foreach (var type in assembly.GetTypes())
            {
                EnsureType(type);
            }
        }

        while (typesToCheck.Count > 0)
        {
            var t = typesToCheck.Pop();
            GatherTypesFrom(t);

            PerformRuntimeCheck(t);
        }
    }

    private HashSet<Assembly> assemblies = new HashSet<Assembly>();

    private Stack<Type> typesToCheck = new Stack<Type>();
    private HashSet<Type> typesKnown = new HashSet<Type>();

    private void EnsureType(Type t)
    {
        // Don't check for assembly here; we can pass f.ex. System.Lazy<Our.T<MyClass>>
        if (t != null && !t.IsGenericTypeDefinition && typesKnown.Add(t))
        {
            typesToCheck.Push(t);

            if (t.IsGenericType)
            {
                foreach (var par in t.GetGenericArguments())
                {
                    EnsureType(par);
                }
            }

            if (t.IsArray)
            {
                EnsureType(t.GetElementType());
            }
        }

    }

    private void PerformRuntimeCheck(Type t)
    {
        if (t.IsGenericType && !t.IsGenericTypeDefinition)
        {
            // Only check the assemblies we explicitly asked for:
            if (this.assemblies.Contains(t.Assembly))
            {
                // Gather the generics data:
                var def = t.GetGenericTypeDefinition();
                var par = def.GetGenericArguments();
                var args = t.GetGenericArguments();

                // Perform checks:
                for (int i = 0; i < args.Length; ++i)
                {
                    foreach (var check in par[i].GetCustomAttributes(typeof(ConstraintAttribute), true).Cast<ConstraintAttribute>())
                    {
                        if (!check.Check(args[i]))
                        {
                            string error = "Runtime type check failed for type " + t.ToString() + ": " + check.ToString();

                            Debugger.Break();
                            throw new ConstraintFailedException(error);
                        }
                    }
                }
            }
        }
    }

    // Phase 1: all types that are referenced in some way
    private void GatherTypesFrom(Type t)
    {
        EnsureType(t.BaseType);

        foreach (var intf in t.GetInterfaces())
        {
            EnsureType(intf);
        }

        foreach (var nested in t.GetNestedTypes())
        {
            EnsureType(nested);
        }

        var all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
        foreach (var field in t.GetFields(all))
        {
            EnsureType(field.FieldType);
        }
        foreach (var property in t.GetProperties(all))
        {
            EnsureType(property.PropertyType);
        }
        foreach (var evt in t.GetEvents(all))
        {
            EnsureType(evt.EventHandlerType);
        }
        foreach (var ctor in t.GetConstructors(all))
        {
            foreach (var par in ctor.GetParameters())
            {
                EnsureType(par.ParameterType);
            }

            // Phase 2: all types that are used in a body
            GatherTypesFrom(ctor);
        }
        foreach (var method in t.GetMethods(all))
        {
            if (method.ReturnType != typeof(void))
            {
                EnsureType(method.ReturnType);
            }

            foreach (var par in method.GetParameters())
            {
                EnsureType(par.ParameterType);
            }

            // Phase 2: all types that are used in a body
            GatherTypesFrom(method);
        }
    }

    private void GatherTypesFrom(MethodBase method)
    {
        if (this.assemblies.Contains(method.DeclaringType.Assembly)) // only consider methods we've build ourselves
        {
            MethodBody methodBody = method.GetMethodBody();
            if (methodBody != null)
            {
                // Handle local variables
                foreach (var local in methodBody.LocalVariables)
                {
                    EnsureType(local.LocalType);
                }

                // Handle method body
                var il = methodBody.GetILAsByteArray();
                if (il != null)
                {
                    foreach (var oper in ILDecompiler.Decompile(method, il))
                    {
                        if (oper.Operand is MemberInfo)
                        {
                            foreach (var type in HandleMember((MemberInfo)oper.Operand))
                            {
                                EnsureType(type);
                            }

                        }
                    }
                }
            }
        }
    }

    private static IEnumerable<Type> HandleMember(MemberInfo info)
    {
        // Event, Field, Method, Constructor or Property.
        yield return info.DeclaringType;
        if (info is EventInfo)
        {
            yield return ((EventInfo)info).EventHandlerType;
        }
        else if (info is FieldInfo)
        {
            yield return ((FieldInfo)info).FieldType;
        }
        else if (info is PropertyInfo)
        {
            yield return ((PropertyInfo)info).PropertyType;
        }
        else if (info is ConstructorInfo)
        {
            foreach (var par in ((ConstructorInfo)info).GetParameters())
            {
                yield return par.ParameterType;
            }
        }
        else if (info is MethodInfo)
        {
            foreach (var par in ((MethodInfo)info).GetParameters())
            {
                yield return par.ParameterType;
            }
        }
        else if (info is Type)
        {
            yield return (Type)info;
        }
        else
        {
            throw new NotSupportedException("Incorrect unsupported member type: " + info.GetType().Name);
        }
    }
}

কোড ব্যবহার করে

ঠিক আছে, এটি সহজ অংশ :-)

// Create something illegal
public class Bar2 : IMyInterface
{
    public void Execute()
    {
        throw new NotImplementedException();
    }
}

// Our fancy check
public class Foo<[IsInterface] T>
{
}

class Program
{
    static Program()
    {
        // Perform all runtime checks
        new NCTChecks(typeof(Program));
    }

    static void Main(string[] args)
    {
        // Normal operation
        Console.WriteLine("Foo");
        Console.ReadLine();
    }
}

8

আপনি এটি সি # এর কোনও প্রকাশিত সংস্করণে বা আসন্ন সি # 4.0.0 এ করতে পারবেন না। এটি কোনও সি # সীমাবদ্ধতা নয়, - সিএলআর নিজেই কোনও "ইন্টারফেস" বাধা নেই।


6

সম্ভব হলে আমি এর মতো একটি সমাধান নিয়ে চলেছি। এটি কেবলমাত্র যদি আপনি কয়েকটি নির্দিষ্ট ইন্টারফেস চান (যেমন আপনার উত্স অ্যাক্সেস রয়েছে) জেনেরিক প্যারামিটার হিসাবে পাস করতে চান তবে তা নয়।

  • আমি আমার ইন্টারফেসগুলি, যা প্রশ্নে এসেছে, একটি খালি ইন্টারফেসের উত্তরাধিকারী হতে দিয়েছি IInterface
  • জেনেরিক টি প্যারামিটারটি সীমাবদ্ধ করেছিলাম IInterface

উত্স হিসাবে, এটি এর মতো দেখাচ্ছে:

  • জেনেরিক প্যারামিটার হিসাবে আপনি যে কোনও ইন্টারফেসটি পাস করতে চান:

    public interface IWhatever : IInterface
    {
        // IWhatever specific declarations
    }
  • IInterface:

    public interface IInterface
    {
        // Nothing in here, keep moving
    }
  • আপনি যে শ্রেণীর উপর টাইপ সীমাবদ্ধতা রাখতে চান:

    public class WorldPeaceGenerator<T> where T : IInterface
    {
        // Actual world peace generating code
    }

এটি খুব বেশি অর্জন করে না। আপনার Tইন্টারফেসের জন্য সীমাবদ্ধ নয়, এটি প্রয়োগকারী কোনও কিছুর মধ্যেই সীমাবদ্ধ IInterface- যেটি চাইলে যে কোনও ধরণের কাজ করতে পারে, যেমন struct Foo : IInterfaceযেহেতু আপনার IInterfaceসর্বাধিক সম্ভাব্য সর্বজনীন (অন্যথায় যা এটি গ্রহণ করে তা অভ্যন্তরীণ হতে হবে)।
আনোরজাকেন

আপনি যে কোনও উপায়ে যেভাবেই গ্রহণ করতে চান তা যদি আপনি নিয়ন্ত্রণ করেন তবে আপনি সমস্ত উপযুক্ত ওভারলোডগুলি তৈরি করতে কোড-জেনারেশন ব্যবহার করতে পারেন, যার সবগুলি কেবল একটি জেনারিক ব্যক্তিগত পদ্ধতিতে পুনঃনির্দেশ করে।
আনোরজাকেন

2

আপনি যা স্থির করেছেন তা হ'ল আপনি যা করতে পারেন তা সর্বোত্তম:

public bool Foo<T>() where T : IBase;

2

আমি অনুরূপ কিছু করার চেষ্টা করেছি এবং একটি কার্যনির্বাহী সমাধান ব্যবহার করেছি: আমি কাঠামোর উপর অন্তর্নিহিত এবং স্পষ্টতাল অপারেটর সম্পর্কে চিন্তা করেছি: ধারণাটি হ'ল টাইপটিকে এমন কাঠামোর মধ্যে মোড়ানো যা প্রচ্ছন্নভাবে টাইপে রূপান্তর করা যায়।

এখানে যেমন একটি কাঠামো:

পাবলিক স্ট্রাক্ট ইন্টারফেস টাইপ - প্রাইভেট টাইপ _ টাইপ;

public InterfaceType(Type type)
{
    CheckType(type);
    _type = type;
}

public static explicit operator Type(InterfaceType value)
{
    return value._type;
}

public static implicit operator InterfaceType(Type type)
{
    return new InterfaceType(type);
}

private static void CheckType(Type type)
{
    if (type == null) throw new NullReferenceException("The type cannot be null");
    if (!type.IsInterface) throw new NotSupportedException(string.Format("The given type {0} is not an interface, thus is not supported", type.Name));
}

}

প্রাথমিক ব্যবহার:

// OK
InterfaceType type1 = typeof(System.ComponentModel.INotifyPropertyChanged);

// Throws an exception
InterfaceType type2 = typeof(WeakReference);

আপনার নিজের চারপাশে আপনার নিজস্ব মেকানিজম কল্পনা করতে হবে, তবে উদাহরণ হতে পারে কোনও পদ্ধতির পরিবর্তে প্যারামিটারে ইন্টারফেস টাইপ নেওয়া একটি পদ্ধতি হতে পারে

this.MyMethod(typeof(IMyType)) // works
this.MyMethod(typeof(MyType)) // throws exception

ওভাররাইড করার একটি পদ্ধতি যা ইন্টারফেসের প্রকারগুলি প্রদান করে:

public virtual IEnumerable<InterfaceType> GetInterfaces()

জেনেরিকগুলির সাথেও করার মতো জিনিস থাকতে পারে তবে আমি চেষ্টা করি নি

আশা করি এটি সহায়তা করতে পারে বা ধারণা দেয় :-)


0

সমাধান এ: এই বাধাগুলির সংমিশ্রণের গ্যারান্টি দেওয়া উচিত TInterfaceএটি একটি ইন্টারফেস:

class example<TInterface, TStruct>
    where TStruct : struct, TInterface
    where TInterface : class
{ }

সাক্ষীরূপে এটির TStructপ্রমাণ দেওয়ার জন্য এটির একটি একক কাঠামো প্রয়োজনTInterface এটি স্ট্রাক্ট।

আপনি আপনার সমস্ত অ-জেনেরিক প্রকারের জন্য সাক্ষী হিসাবে একক কাঠামো ব্যবহার করতে পারেন:

struct InterfaceWitness : IA, IB, IC 
{
    public int DoA() => throw new InvalidOperationException();
    //...
}

সমাধান বি: আপনি যদি সাক্ষী হিসাবে স্ট্রাইক তৈরি করতে না চান তবে আপনি একটি ইন্টারফেস তৈরি করতে পারেন

interface ISInterface<T>
    where T : ISInterface<T>
{ }

এবং একটি সীমাবদ্ধতা ব্যবহার করুন:

class example<TInterface>
    where TInterface : ISInterface<TInterface>
{ }

ইন্টারফেসের জন্য বাস্তবায়ন:

interface IA :ISInterface<IA>{ }

এটি কিছু সমস্যা সমাধান করে, তবে আস্থা দরকার যে ISInterface<T>নন-ইন্টারফেস প্রকারের জন্য নুন প্রয়োগ করে না, তবে দুর্ঘটনাক্রমে এটি করা খুব কঠিন।


-4

পরিবর্তে একটি বিমূর্ত শ্রেণি ব্যবহার করুন। সুতরাং, আপনার মত কিছু হবে:

public bool Foo<T>() where T : CBase;

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