C # में टाइपडिफ के बराबर


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

को छोड़कर, मेरे मामले में, मैं पहले से ही एक जटिल प्रकार का उपयोग कर रहा था, न कि केवल एक इंट। यह अच्छा होगा यदि इसे थोड़ा सरल करना संभव हो ...

संपादित करें: अर्थात। शायद इसी तरह के व्यवहार को प्राप्त करने के लिए इसे फिर से परिभाषित करने की आवश्यकता के बजाय EventHandler टाइप करें।

जवाबों:


341

नहीं, वहाँ typedef का कोई सच्चा समकक्ष नहीं है। आप एक फ़ाइल में 'निर्देशों' का उपयोग कर सकते हैं, जैसे

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

लेकिन यह केवल उस स्रोत फ़ाइल को प्रभावित करेगा। C और C ++ में, मेरा अनुभव यह है कि typedefआमतौर पर .h फ़ाइलों के भीतर उपयोग किया जाता है, जो व्यापक रूप से शामिल हैं - इसलिए एक एकल typedefका उपयोग पूरी परियोजना पर किया जा सकता है। यह क्षमता C # में मौजूद नहीं है, क्योंकि #includeC # में कोई कार्यक्षमता नहीं है जो आपको usingनिर्देश को एक फ़ाइल से दूसरी फ़ाइल में शामिल करने की अनुमति देगा ।

सौभाग्य से, उदाहरण आप दे करता अंतर्निहित विधि समूह रूपांतरण - इसका कोई सुधार। आप अपनी ईवेंट सब्सक्रिप्शन लाइन को केवल:

gcInt.MyEvent += gcInt_MyEvent;

:)


11
मैं हमेशा यह भूल जाता हूं कि आप ऐसा कर सकते हैं। हो सकता है क्योंकि Visual Studio अधिक वर्बोज़ संस्करण का सुझाव देता है। लेकिन मैं हैंडलर का नाम टाइप करने के बजाय दो बार TAB दबाने के साथ ठीक हूं;)
ओरेगॉनहोस्ट

11
मेरे अनुभव में (जो दुर्लभ है), आपको उदाहरण के लिए पूरी तरह से योग्य प्रकार का नाम निर्दिष्ट करना होगा: using MyClassDictionary = System.Collections.Generic.Dictionary<System.String, MyNamespace.MyClass>; क्या यह सही है? अन्यथा यह usingइसके ऊपर की परिभाषाओं पर विचार नहीं करता है।
टन्नुज

3
मैं typedef uint8 myuuid[16];"निर्देश" का उपयोग करके परिवर्तित नहीं कर सका । using myuuid = Byte[16];संकलन नहीं है। usingकेवल प्रकार के उपनाम बनाने के लिए उपयोग किया जा सकता है । typedefयह और अधिक लचीला प्रतीत होता है, क्योंकि यह एक संपूर्ण घोषणा (सरणी आकार सहित) के लिए एक उपनाम बना सकता है। क्या इस मामले में कोई विकल्प है?
natenho

2
@natenho: वास्तव में नहीं। निकटतम आप एक निश्चित आकार के बफर के साथ एक संरचना के लिए आ सकते हैं, शायद।
जॉन स्कीट

1
@tunnuz जब तक आप इसे किसी नाम स्थान के अंदर निर्दिष्ट नहीं करते हैं
जॉन स्मिथ

38

जॉन ने वास्तव में एक अच्छा समाधान दिया, मुझे नहीं पता था कि आप ऐसा कर सकते हैं!

कई बार मैंने जो सहारा लिया, वह वर्ग से विरासत में मिला और उसके निर्माणकर्ता थे। उदाहरण के लिए

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

सबसे अच्छा समाधान नहीं (जब तक कि आपकी विधानसभा अन्य लोगों द्वारा उपयोग नहीं की जाती है), लेकिन यह काम करता है।


41
निश्चित रूप से एक अच्छी विधि है, लेकिन ध्यान रखें कि उन (कष्टप्रद) सील प्रकार मौजूद हैं, और यह वहां काम नहीं करेगा। मैं वास्तव में कामना करता हूं कि C # पहले से ही टाइपडेफ का परिचय दे। यह (विशेष रूप से C ++ प्रोग्रामर्स के लिए) एक सख्त जरूरत है।
मास्टरमैटिक

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

मैं वास्तव में इसका समर्थन नहीं करता और कभी भी इस तरह का प्रयोग नहीं किया है, लेकिन यह शायद कुछ विशिष्ट परिस्थितियों के लिए काम कर सकता है।


धन्यवाद @palswim, मैं यहाँ कुछ "टाइप्डिफ़ स्ट्रिंग पहचानकर्ता" की तलाश में मिला; इसलिए आपका सुझाव सिर्फ वही हो सकता है जिसकी मुझे आवश्यकता है।
योयो

6

C # इवेंट डेलीगेट्स के लिए विरासत में मिली कुछ सहूलियत का समर्थन करता है, इसलिए एक तरीका इस प्रकार है:

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

अपने ईवेंट को सब्सक्राइब करने के लिए इस्तेमाल किया जा सकता है, कोई स्पष्ट कास्ट की आवश्यकता नहीं है

gcInt.MyEvent += LowestCommonHander;

आप लैम्ब्डा सिंटैक्स का भी उपयोग कर सकते हैं और इन्टेलिजेंस आप सभी के लिए किया जाएगा:

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

मुझे गंभीरता से लिनक पर एक अच्छी नज़र रखने की आवश्यकता है ... हालांकि रिकॉर्ड के लिए, मैं उस समय 2.0 के लिए निर्माण कर रहा था (वीएस 2008 में हालांकि)
मैथ्यू शारले

ओह, इसके अलावा, मैं ठीक सदस्यता ले सकता हूं, लेकिन फिर घटना के तर्क प्राप्त करने के लिए मुझे एक स्पष्ट कलाकार की आवश्यकता है, और अधिमानतः चेकिंग कोड टाइप करें, बस सुरक्षित पक्ष पर होने के लिए।
मैथ्यू शारले

9
सिंटैक्स सही है, लेकिन मैं यह नहीं कहूंगा कि यह "लाइनक सिंटैक्स" है; बल्कि यह एक लंबोदर अभिव्यक्ति है। लैम्ब्डा एक सहायक विशेषता है जो लिनक को काम करती है, लेकिन इसके बारे में पूरी तरह से स्वतंत्र हैं। अनिवार्य रूप से, कहीं भी आप एक प्रतिनिधि का उपयोग कर सकते हैं, आप एक लंबोदर अभिव्यक्ति का उपयोग कर सकते हैं।
स्कॉट डोरमैन

फेयर पॉइंट, मुझे लंबोदर कहना चाहिए था। एक प्रतिनिधि .Net 2 में काम करेगा, लेकिन आपको स्पष्ट रूप से नेस्टेड जेनेरिक प्रकार को फिर से घोषित करने की आवश्यकता होगी।
कीथ

5

मुझे लगता है कि कोई टाइपफ़ेड नहीं है। आप केवल एक विशिष्ट प्रतिनिधि प्रकार को जेनेरिकक्लास में जेनेरिक के बजाय परिभाषित कर सकते हैं, अर्थात

public delegate GenericHandler EventHandler<EventData>

यह इसे कम कर देगा। लेकिन निम्नलिखित सुझाव के बारे में क्या:

विजुअल स्टूडियो का उपयोग करें। इस तरह, जब आपने टाइप किया

gcInt.MyEvent += 

यह पहले से ही Intellisense से संपूर्ण ईवेंट हैंडलर हस्ताक्षर प्रदान करता है। TAB दबाएँ और यह वहाँ है। उत्पन्न हैंडलर नाम को स्वीकार करें या इसे बदलें, और फिर हैंडलर स्टब को ऑटो-जनरेट करने के लिए फिर से TAB दबाएं।


2
हाँ, मैंने उदाहरण प्रस्तुत करने के लिए यही किया है। लेकिन इस तथ्य को फिर से देखने पर वापस आना तथ्य को भ्रमित कर सकता है।
मैथ्यू शार्ले

मुझे पता है कि आपका क्या आशय है। यही कारण है कि मैं अपने इवेंट हस्ताक्षर कम रखना पसंद करता हूं, या अपने स्वयं के प्रतिनिधि प्रकार के बजाय जेनेरिक इवेंटहैंडलर <टी> का उपयोग करने के लिए एफएक्सकोप सिफारिश से दूर हो जाता हूं। लेकिन फिर, जॉन स्कीट द्वारा प्रदान किए गए लघु-हाथ संस्करण के साथ छड़ी :)
ओरेगॉनहोस्ट

2
यदि आपने ReSharper प्राप्त कर लिया है, तो यह आपको बताएगा कि लंबा संस्करण ओवरकिल है (इसे ग्रे में रंगकर), और आप इसे फिर से छुटकारा पाने के लिए "क्विक फिक्स" का उपयोग कर सकते हैं।
रोजर लिप्सकॉम्ब

5

C ++ और C # दोनों एक नए प्रकार का निर्माण करने के लिए आसान तरीके याद कर रहे हैं जो कि एक टाइपिंग के प्रकार के समान है। मुझे लगता है कि टाइप-सेफ प्रोग्रामिंग के लिए इस तरह के 'टाइपफेड' पूरी तरह से आवश्यक हैं और इसका वास्तविक शर्म की बात यह नहीं है कि उनके पास बिल्ट-इन नहीं है। के बीच अंतर void f(string connectionID, string username)करने के लिए void f(ConID connectionID, UserName username)स्पष्ट है ...

(आप BOOST_STRONG_TYPEDEF में बढ़ावा देने के साथ C ++ में कुछ समान हासिल कर सकते हैं)

यह उत्तराधिकार का उपयोग करने के लिए आकर्षक हो सकता है लेकिन इसकी कुछ प्रमुख सीमाएँ हैं:

  • यह आदिम प्रकारों के लिए काम नहीं करेगा
  • व्युत्पन्न प्रकार को अभी भी मूल प्रकार में डाला जा सकता है, अर्थात हम इसे अपने मूल प्रकार को प्राप्त करने वाले फ़ंक्शन में भेज सकते हैं, यह पूरे उद्देश्य को हरा देता है
  • हम सील की गई कक्षाओं से नहीं निकल सकते (और कई .NET क्लास सील हैं)

C # में एक समान चीज़ को प्राप्त करने का एकमात्र तरीका एक नए वर्ग में हमारे प्रकार की रचना है:

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

क्या कुछ जटिल जैसे stackoverflow.com/questions/50404586/… के लिए लाइक टाइप काम करेगा ? मैंने इसके साथ खेलने की कोशिश की है और काम करने वाला क्लास सेटअप नहीं पा सकता।
जे क्रोगन

यह वास्तव में LikeTypeपुस्तकालय का इरादा नहीं है । LikeTypeप्राथमिक उद्देश्य प्रिमिटिव ऑब्सेशन के साथ मदद करना है , और इस तरह, यह नहीं चाहता है कि आप लिपटे हुए प्रकार के आसपास से गुजरने में सक्षम हों, जैसे कि यह रैपर प्रकार था। जैसे कि, अगर मैं बनाता हूं, Age : LikeType<int>तो यदि मेरे फ़ंक्शन को ए की जरूरत है Age, तो मैं यह सुनिश्चित करना चाहता हूं कि मेरे कॉलर्स ए पास कर रहे हैं Age, ए नहीं int
मैट क्लेन

कहा जा रहा है, मुझे लगता है कि मेरे पास आपके प्रश्न का उत्तर है, जिसे मैं वहां पोस्ट करूंगा।
मैट क्लेन

3

यहाँ इसके लिए कोड है, आनंद लें !, मैंने नाम स्थान रेखा 106 के अंदर "नेटिव" स्टेटमेंट का उपयोग करके "नेटवर्थ" टाइप से उठाया है

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मुझे C # में मिला है using। उदाहरण के लिए, मैं इस कोड के साथ संकलक झंडे के माध्यम से फ्लोट सटीक को नियंत्रित कर सकता हूं:

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

दुर्भाग्य से, यह आवश्यक है कि आप इसे हर उस फ़ाइल के शीर्ष पर रखें जहाँ आप उपयोग करते हैं real_t। वर्तमान में C # में वैश्विक नामस्थान प्रकार घोषित करने का कोई तरीका नहीं है।

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