সি # তে টাইপিডের সমতুল্য


326

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

class GenericClass<T> 
{
    public event EventHandler<EventData> MyEvent;
    public class EventData : EventArgs { /* snip */ }
    // ... snip
}

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

GenericClass<int> gcInt = new GenericClass<int>;
gcInt.MyEvent += new EventHandler<GenericClass<int>.EventData>(gcInt_MyEvent);
// ...

private void gcInt_MyEvent(object sender, GenericClass<int>.EventData e)
{
    throw new NotImplementedException();
}

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

সম্পাদনা: অর্থাৎ। সম্ভবত ইভেন্টহ্যান্ডলারকে অনুরূপ আচরণের জন্য এটি পুনরায় সংজ্ঞায়িত করার পরিবর্তে টাইপফাইফিং করা হচ্ছে।

উত্তর:


341

না, টাইপডেফের সত্যিকারের সমতুল্য নেই। আপনি একটি ফাইলের মধ্যে 'ব্যবহার করে' নির্দেশাবলী ব্যবহার করতে পারেন, যেমন

using CustomerList = System.Collections.Generic.List<Customer>;

তবে এটি কেবল সেই উত্স ফাইলটিতে প্রভাব ফেলবে। সি এবং C ++, আমার অভিজ্ঞতা যে typedefতাই একটি একক - সাধারণত জ ফাইল যা ব্যাপকভাবে অন্তর্ভুক্ত করা হয় মধ্যে ব্যবহার করা হয় typedefএকটি সম্পূর্ণ প্রকল্প নিয়ে ব্যবহার করা যাবে। এই ক্ষমতাটি সি # তে বিদ্যমান নেই, কারণ সি # তে কোনও #includeকার্যকারিতা নেই যা আপনাকে usingঅন্য একটি ফাইলের দিকনির্দেশগুলি অন্তর্ভুক্ত করতে দেয় ।

ভাগ্যক্রমে, আপনি যে উদাহরণটি দিয়েছেন তার একটি স্থির রয়েছে - অন্তর্নিহিত পদ্ধতি গ্রুপ রূপান্তর। আপনি আপনার ইভেন্টের সাবস্ক্রিপশন লাইনটিকে কেবল:

gcInt.MyEvent += gcInt_MyEvent;

:)


11
আমি সবসময় ভুলে যাই যে আপনি এটি করতে পারেন। ভিজ্যুয়াল স্টুডিও আরও ভার্জোজ সংস্করণ প্রস্তাব দেয় কারণ হতে পারে। তবে হ্যান্ডলারের নাম টাইপ না করে দুবার
টিএবি টিপতে

11
আমার অভিজ্ঞতায় (যা দুর্লভ), আপনাকে পুরোপুরি যোগ্যতাযুক্ত টাইপের নাম উল্লেখ করতে হবে, উদাহরণস্বরূপ: using MyClassDictionary = System.Collections.Generic.Dictionary<System.String, MyNamespace.MyClass>; এটি কি সঠিক? অন্যথায় এটি এর usingউপরে সংজ্ঞাগুলি বিবেচনা করে বলে মনে হয় না ।
টিউনুজ

3
আমি typedef uint8 myuuid[16];"ব্যবহার করে" নির্দেশের মাধ্যমে রূপান্তর করতে পারিনি । using myuuid = Byte[16];সংকলন করে না প্রকারের উপকরণusing তৈরি করার জন্য ব্যবহার করা যেতে পারে । এটি অনেক বেশি নমনীয় বলে মনে হচ্ছে, যেহেতু এটি একটি সম্পূর্ণ ঘোষণার (অ্যারের মাপ সহ) একটি উপকরণ তৈরি করতে পারে। এই ক্ষেত্রে কোন বিকল্প আছে? typedef
নাটেনহো

2
@ নাটেনহো: আসলেই নয়। আপনি যে নিকটে আসতে পারেন সেটি সম্ভবত একটি স্থির আকারের বাফারের সাথে একটি কাঠামো থাকবে।
জন স্কিটি

1
@ ভাগ্নুজ যদি না আপনি কোনও নেমস্পেসের ভিতরে উল্লেখ না করেন
জন স্মিথ

38

জন সত্যিই একটি দুর্দান্ত সমাধান দিয়েছেন, আমি জানতাম না যে আপনি এটি করতে পারেন!

মাঝে মাঝে আমি যা অবলম্বন করতাম তা ক্লাস থেকে উত্তরাধিকার সূত্রে হয় এবং এর নির্মাণকারী তৈরি করত। যেমন

public class FooList : List<Foo> { ... }

সেরা সমাধান নয় (যদি না আপনার সমাবেশটি অন্য লোকেরা ব্যবহার করে) তবে তা কার্যকর হয়।


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

1
আমি লাইকটাইপ নামক এই পরিস্থিতির জন্য একটি প্রকল্প তৈরি করেছি যা উত্তরাধিকার সূত্রে প্রাপ্ত হওয়ার পরিবর্তে অন্তর্নিহিত ধরণটি মোড়ানো। এছাড়া পরোক্ষভাবে রূপান্তর করবে করার অন্তর্নিহিত টাইপ যাতে আপনি ভালো কিছু ব্যবহার করতে পারে public class FooList : LikeType<IReadOnlyList<Foo>> { ... }এবং তারপর এটি ব্যবহার যে কোন জায়গায় আপনি যদি একটি আশা IReadOnlyList<Foo>। নীচে আমার উত্তর আরও বিশদ দেখায়।
ম্যাট ক্লিন

3
Fooউদাহরণস্বরূপ যে টেম্পলেট পদ্ধতি গ্রহণ করে তা পাস হলে এটি প্রকারটিও অনুমান করে না List<T>। সঠিক টাইপিডেফ দিয়ে এটি সম্ভব হবে।
আলেক্সি পেট্রেনকো

18

আপনি যদি জানেন যে আপনি কী করছেন, তবে আপনি উপন্যাস ক্লাস এবং প্রকৃত শ্রেণীর মধ্যে রূপান্তর করতে অন্তর্ভুক্ত অপারেটরগুলির সাথে একটি শ্রেণি নির্ধারণ করতে পারেন।

class TypedefString // Example with a string "typedef"
{
    private string Value = "";
    public static implicit operator string(TypedefString ts)
    {
        return ((ts == null) ? null : ts.Value);
    }
    public static implicit operator TypedefString(string val)
    {
        return new TypedefString { Value = val };
    }
}

আমি আসলে এটিকে সমর্থন করি না এবং এর আগে কখনও ব্যবহার করি নি, তবে এটি সম্ভবত কিছু নির্দিষ্ট পরিস্থিতিতে কাজ করতে পারে।


ধন্যবাদ @ পালসুইম, আমি এখানে "টাইপডিফ স্ট্রিং আইডেন্টিফায়ার" এর মতো কিছু খুঁজছিলাম; সুতরাং আপনার পরামর্শটি আমার প্রয়োজন অনুযায়ী হতে পারে।
yoyo

6

সি # ইভেন্ট প্রতিনিধিদের জন্য কিছু উত্তরাধিকার সূত্রে সমর্থন করে, সুতরাং এর মতো একটি পদ্ধতি:

void LowestCommonHander( object sender, EventArgs e ) { ... } 

আপনার ইভেন্টের সদস্যতা নিতে ব্যবহার করা যেতে পারে, কোনও সুস্পষ্ট কাস্টের প্রয়োজন নেই

gcInt.MyEvent += LowestCommonHander;

আপনি এমনকি ল্যাম্বদা সিনট্যাক্স ব্যবহার করতে পারেন এবং ইন্টেলিসেন্স সব আপনার জন্য করা হবে:

gcInt.MyEvent += (sender, e) =>
{
    e. //you'll get correct intellisense here
};

লিনকের দিকে আমার ভালো নজর দেওয়া দরকার ... যদিও রেকর্ডটির জন্য, আমি সেই সময় 2.0 এর জন্য তৈরি করছিলাম (যদিও ২০০৮ সালে)
ম্যাথু শার্লি

ওহ, এছাড়াও, আমি সূক্ষ্ম সাবস্ক্রাইব করতে পারি, তবে তারপরে ইভেন্টের যুক্তিগুলি পেতে আমার একটি স্পষ্ট কাস্ট প্রয়োজন, এবং কেবলমাত্র নিরাপদ পাশে থাকতে চেকিং কোডটি টাইপ করুন।
ম্যাথু শার্লে 13

9
বাক্য গঠনটি সঠিক, তবে আমি এটি "লিনক সিনট্যাক্স" বলব না; বরং এটি ল্যাম্বডা এক্সপ্রেশন। লাম্বদাস একটি সমর্থনকারী বৈশিষ্ট্য যা লিনককে কাজ করে তবে এটি থেকে সম্পূর্ণ স্বাধীন। মূলত, আপনি যে কোনও জায়গায় প্রতিনিধি ব্যবহার করতে পারেন, আপনি ল্যাম্বডা এক্সপ্রেশন ব্যবহার করতে পারেন।
স্কট ডরম্যান 14

ফেয়ার পয়েন্ট, আমি ল্যাম্বডা বলা উচিত ছিল। একটি প্রতিনিধি। নেট 2 এ কাজ করবে, তবে আপনাকে স্পষ্ট করে আবার নেস্টেড জেনেরিক ধরণের ঘোষণা করতে হবে।
কিথ

5

আমার মনে হয় কোনও টাইপেইফ নেই। আপনি কেবল জেনেরিক ক্লাসে জেনেরিকের পরিবর্তে একটি নির্দিষ্ট প্রতিনিধি প্রকারের সংজ্ঞা দিতে পারেন, অর্থাত্‍

public delegate GenericHandler EventHandler<EventData>

এটি এটি খাটো করে তুলবে। তবে নিম্নলিখিত পরামর্শ সম্পর্কে কি:

ভিজ্যুয়াল স্টুডিও ব্যবহার করুন। আপনি টাইপ করার সময় এইভাবে

gcInt.MyEvent += 

এটি ইন্টেলিসেন্স থেকে ইতিমধ্যে সম্পূর্ণ ইভেন্ট হ্যান্ডলার স্বাক্ষর সরবরাহ করে। ট্যাব টিপুন এবং এটি সেখানে। উত্পন্ন হ্যান্ডলারের নাম গ্রহণ করুন বা এটি পরিবর্তন করুন এবং হ্যান্ডলার স্টাবটি স্বয়ংক্রিয়ভাবে উত্পন্ন করতে আবার টিএবি টিপুন।


2
হ্যাঁ, উদাহরণটি উত্পন্ন করতে আমি এটিই করেছি। তবে এটিকে আবার দেখার জন্য ফিরে আসা সত্যটি এখনও বিভ্রান্তিকর হতে পারে।
ম্যাথু শার্লে 9

আমি জানি তুমি কী বোঝাতে চাও। এ কারণেই আমি আমার ইভেন্টের স্বাক্ষরগুলি সংক্ষিপ্ত রাখতে বা আমার নিজস্ব প্রতিনিধি প্রকারের পরিবর্তে জেনেরিক ইভেন্টহ্যান্ডলার <টি> ব্যবহার করার জন্য FxCop এর পরামর্শ থেকে দূরে যেতে চাই। তবে তারপরে, জোন স্কিট :)
ওরেগনগোস্টের

2
আপনি যদি রিশার্পার পেয়ে থাকেন তবে এটি আপনাকে জানাবে যে দীর্ঘ সংস্করণটি ওভারকিল (ধূসর রঙে রঙ করে), এবং আপনি এটি থেকে মুক্তি পাওয়ার জন্য একটি "দ্রুত ফিক্স" ব্যবহার করতে পারেন।
রজার লিপসকমবে

5

সি ++ এবং সি # উভয়ই একটি নতুন টাইপ তৈরির সহজ উপায় অনুপস্থিত যা একটি বিদ্যমান ধরণের মতো শব্দার্থগতভাবে অভিন্ন। টাইপ-সেফ প্রোগ্রামিংয়ের জন্য আমি এই জাতীয় 'টাইপিডেফস' সম্পূর্ণরূপে অপরিহার্য পাই এবং এটির সত্যিকারের লজ্জাজনক সি # তাদের অন্তর্নির্মিত নেই। মধ্যে পার্থক্য void f(string connectionID, string username)করার জন্য void f(ConID connectionID, UserName username)সুস্পষ্ট ...

(আপনি BOOST_STRONG_TYPEDEF এ উন্নীত করে সি ++ তে অনুরূপ কিছু অর্জন করতে পারেন)

উত্তরাধিকার ব্যবহারে এটি লোভনীয় হতে পারে তবে এর কিছু বড় সীমাবদ্ধতা রয়েছে:

  • এটি আদিম ধরণের জন্য কাজ করবে না
  • উদ্ভূত প্রকারটি এখনও মূল ধরণে কাস্ট করা যেতে পারে, অর্থাত আমরা এটি আমাদের মূল ধরণ প্রাপ্ত কোনও ফাংশনে প্রেরণ করতে পারি, এটি পুরো উদ্দেশ্যকে পরাস্ত করে
  • আমরা সিল করা ক্লাস থেকে উদ্ভব করতে পারি না (এবং উদাহরণস্বরূপ অনেক। নেট ক্লাস সিল করা হয়)

সি # তে একই জিনিস অর্জনের একমাত্র উপায় হ'ল আমাদের ধরণের নতুন ক্লাসে রচনা করে:

Class SomeType { 
  public void Method() { .. }
}

sealed Class SomeTypeTypeDef {
  public SomeTypeTypeDef(SomeType composed) { this.Composed = composed; }

  private SomeType Composed { get; }

  public override string ToString() => Composed.ToString();
  public override int GetHashCode() => HashCode.Combine(Composed);
  public override bool Equals(object obj) => obj is TDerived o && Composed.Equals(o.Composed); 
  public bool Equals(SomeTypeTypeDefo) => object.Equals(this, o);

  // proxy the methods we want
  public void Method() => Composed.Method();
}

এটি যখন কাজ করবে এটি কেবল একটি টাইপডেফের জন্য খুব ভার্জোজ। এছাড়াও সিরিয়ালাইজেশন (যেমন জসন থেকে) নিয়ে আমাদের একটি সমস্যা রয়েছে কারণ আমরা শ্রেণিবর্গটি তার রচিত সম্পত্তিটির মাধ্যমে ক্রমিকায়িত করতে চাই।

নীচে একটি সহায়ক শ্রেণি রয়েছে যা এটিকে আরও সহজ করতে "কৌতূহলীভাবে পুনরাবৃত্তি টেম্পলেট প্যাটার্ন" ব্যবহার করে:

namespace Typedef {

  [JsonConverter(typeof(JsonCompositionConverter))]
  public abstract class Composer<TDerived, T> : IEquatable<TDerived> where TDerived : Composer<TDerived, T> {
    protected Composer(T composed) { this.Composed = composed; }
    protected Composer(TDerived d) { this.Composed = d.Composed; }

    protected T Composed { get; }

    public override string ToString() => Composed.ToString();
    public override int GetHashCode() => HashCode.Combine(Composed);
    public override bool Equals(object obj) => obj is Composer<TDerived, T> o && Composed.Equals(o.Composed); 
    public bool Equals(TDerived o) => object.Equals(this, o);
  }

  class JsonCompositionConverter : JsonConverter {
    static FieldInfo GetCompositorField(Type t) {
      var fields = t.BaseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
      if (fields.Length!=1) throw new JsonSerializationException();
      return fields[0];
    }

    public override bool CanConvert(Type t) {
      var fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
      return fields.Length == 1;
    }

    // assumes Compositor<T> has either a constructor accepting T or an empty constructor
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
      while (reader.TokenType == JsonToken.Comment && reader.Read()) { };
      if (reader.TokenType == JsonToken.Null) return null; 
      var compositorField = GetCompositorField(objectType);
      var compositorType = compositorField.FieldType;
      var compositorValue = serializer.Deserialize(reader, compositorType);
      var ctorT = objectType.GetConstructor(new Type[] { compositorType });
      if (!(ctorT is null)) return Activator.CreateInstance(objectType, compositorValue);
      var ctorEmpty = objectType.GetConstructor(new Type[] { });
      if (ctorEmpty is null) throw new JsonSerializationException();
      var res = Activator.CreateInstance(objectType);
      compositorField.SetValue(res, compositorValue);
      return res;
    }

    public override void WriteJson(JsonWriter writer, object o, JsonSerializer serializer) {
      var compositorField = GetCompositorField(o.GetType());
      var value = compositorField.GetValue(o);
      serializer.Serialize(writer, value);
    }
  }

}

সুরকারের সাথে উপরের শ্রেণিটি সহজ হয়ে যায়:

sealed Class SomeTypeTypeDef : Composer<SomeTypeTypeDef, SomeType> {
   public SomeTypeTypeDef(SomeType composed) : base(composed) {}

   // proxy the methods we want
   public void Method() => Composed.Method();
}

এবং অতিরিক্তভাবে SomeTypeTypeDefজেসনকে একইভাবে সিরিয়ালাইজ করবে SomeType

আশাকরি এটা সাহায্য করবে !


4

আপনি তৈরি করেছেন লাইপটাইপ নামে একটি ওপেন সোর্স লাইব্রেরি এবং নিউগেট প্যাকেজ ব্যবহার করতে পারেন GenericClass<int>যা আপনি সন্ধান করছেন এমন আচরণ আপনাকে দেবে।

কোডটি দেখতে এমন হবে:

public class SomeInt : LikeType<int>
{
    public SomeInt(int value) : base(value) { }
}

[TestClass]
public class HashSetExample
{
    [TestMethod]
    public void Contains_WhenInstanceAdded_ReturnsTrueWhenTestedWithDifferentInstanceHavingSameValue()
    {
        var myInt = new SomeInt(42);
        var myIntCopy = new SomeInt(42);
        var otherInt = new SomeInt(4111);

        Assert.IsTrue(myInt == myIntCopy);
        Assert.IsFalse(myInt.Equals(otherInt));

        var mySet = new HashSet<SomeInt>();
        mySet.Add(myInt);

        Assert.IsTrue(mySet.Contains(myIntCopy));
    }
}

কিছু জটিল জন্য করতেন LikeType কাজ stackoverflow.com/questions/50404586/... ? আমি এটির সাথে খেলতে চেষ্টা করেছি এবং কোনও ক্লাস সেটআপ কাজ করতে পারে না।
জে ক্রোগান

এটি আসলে LikeTypeলাইব্রেরির উদ্দেশ্য নয় । LikeTypeএর প্রাথমিক উদ্দেশ্যটি আদিম আবেশে সহায়তা করা এবং যেমনটি আপনি চান না যে এটি মোড়কের ধরণের মতো মোড়ানো টাইপের চারপাশে যেতে সক্ষম হবেন। যেমনটি, যদি আমি এটি করি Age : LikeType<int>তবে যদি আমার ফাংশনটির একটি প্রয়োজন হয় Age, আমি নিশ্চিত করতে চাই যে আমার কলকারীরা একটি Ageনয়, একটি পাস করছে int
ম্যাট ক্লিন

বলা হচ্ছে, আমি মনে করি আপনার প্রশ্নের উত্তর আমার আছে, যা আমি সেখানে পোস্ট করব।
ম্যাট ক্লিন

3

এখানে এটা কোড ভোগ, !, আমি dotNetReference থেকে কুড়ান টাইপ নামস্থান লাইন 106 ভিতরে "ব্যবহার", বিবৃতি http://referencesource.microsoft.com/#mscorlib/microsoft/win32/win32native.cs

using System;
using System.Collections.Generic;
namespace UsingStatement
{
    using Typedeffed = System.Int32;
    using TypeDeffed2 = List<string>;
    class Program
    {
        static void Main(string[] args)
        {
        Typedeffed numericVal = 5;
        Console.WriteLine(numericVal++);

        TypeDeffed2 things = new TypeDeffed2 { "whatever"};
        }
    }
}

2

সিল না করা ক্লাসগুলির জন্য কেবল তাদের কাছ থেকে উত্তরাধিকার সূত্রে প্রাপ্ত:

public class Vector : List<int> { }

তবে সিল করা ক্লাসগুলির জন্য এ জাতীয় বেস শ্রেণীর সাথে টাইপিডের আচরণ অনুকরণ করা সম্ভব:

public abstract class Typedef<T, TDerived> where TDerived : Typedef<T, TDerived>, new()
{
    private T _value;

    public static implicit operator T(Typedef<T, TDerived> t)
    {
        return t == null ? default : t._value;
    }

    public static implicit operator Typedef<T, TDerived>(T t)
    {
        return t == null ? default : new TDerived { _value = t };
    }
}

// Usage examples

class CountryCode : Typedef<string, CountryCode> { }
class CurrencyCode : Typedef<string, CurrencyCode> { }
class Quantity : Typedef<int, Quantity> { }

void Main()
{
    var canadaCode = (CountryCode)"CA";
    var canadaCurrency = (CurrencyCode)"CAD";
    CountryCode cc = canadaCurrency;        // Compilation error
    Concole.WriteLine(canadaCode == "CA");  // true
    Concole.WriteLine(canadaCurrency);      // CAD

    var qty = (Quantity)123;
    Concole.WriteLine(qty);                 // 123
}

1

typedefআমি সি # তে যে সন্ধান করেছি তার সেরা বিকল্প হ'ল using। উদাহরণস্বরূপ, আমি এই কোডটি সহ কম্পাইলার ফ্ল্যাগের মাধ্যমে ভাসমান নির্ভুলতা নিয়ন্ত্রণ করতে পারি:

#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif

দুর্ভাগ্যবশত, এটা প্রয়োজন যে আপনি উপরের এই জায়গা প্রতিটি ফাইল যেখানে আপনি ব্যবহার real_t। সি # তে গ্লোবাল নেমস্পেসের প্রকার ঘোষণার কোনও উপায় নেই।

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