Эквивалент typedef в C#
есть ли эквивалент typedef в C# или каким-то образом получить какое-то подобное поведение? Я немного погуглил, но везде, где я смотрю, кажется, отрицательным. В настоящее время у меня ситуация похожа на следующую:
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();
}
за исключением того, что в моем случае я уже использовал сложный тип, а не только int. Было бы неплохо, если бы это можно было немного упростить...
Edit: ie. возможно, typedefing EventHandler вместо того, чтобы переопределять его, чтобы получить подобное поведение.
9 ответов:
нет, нет истинного эквивалента typedef. Вы можете использовать директивы 'using' в одном файле, например
using CustomerList = System.Collections.Generic.List<Customer>;но это повлияет только на этот исходный файл. В C и C++, мой опыт заключается в том, что
typedefобычно используется внутри .h файлы, которые включены широко-так что одинtypedefможно использовать над всем проектом. Эта способность не существует в C#, потому что нет#includeфункциональность в C#, которая позволит вам включитьusingдирективы из одного файла в другой.к счастью, пример вы даете тут есть исправление-неявное преобразование группы методов. Вы можете изменить свою строку подписки на события просто:
gcInt.MyEvent += gcInt_MyEvent;:)
Джон действительно дал хорошее решение, я не знал, что вы можете это сделать!
иногда то, к чему я прибегал, наследовалось от класса и создавало его конструкторы. Е. Г.
public class FooList : List<Foo> { ... }Не лучшее решение (если ваша сборка не используется другими людьми), но оно работает.
Если вы знаете, что делаете, вы можете определить класс с неявными операторами для преобразования между классом псевдонима и фактическим классом.
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 }; } }Я на самом деле не одобряю это и никогда не использовал что-то подобное, но это, вероятно, может работать для некоторых конкретных обстоятельств.
C# поддерживает некоторые унаследованные ковариации для делегатов событий, поэтому такой метод:
void LowestCommonHander( object sender, EventArgs e ) { ... }можно использовать для подписки на ваше событие, не требуется явное приведение
gcInt.MyEvent += LowestCommonHander;вы даже можете использовать лямбда-синтаксис и intellisense все будет сделано для вас:
gcInt.MyEvent += (sender, e) => { e. //you'll get correct intellisense here };
Я думаю, что нет typedef. Вы можете определить только определенный тип делегата вместо универсального в GenericClass, т. е.
public delegate GenericHandler EventHandler<EventData>это сделает его короче. Но как насчет следующего предложения:
Используйте Visual Studio. Таким образом, когда вы набрали
gcInt.MyEvent +=он уже предоставляет полную подпись обработчика событий от Intellisense. Нажмите TAB и он там. Примите сгенерированное имя обработчика или измените его, а затем снова нажмите клавишу TAB для автоматического создания заготовки обработчика.
Вы можете использовать библиотеку с открытым исходным кодом и пакет NuGet под названием LikeType что я создал, что даст вам
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)); } }
вот код для него, наслаждайтесь!, Я взял это из dotNetReference введите инструкцию "using" в строке пространства имен 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"}; } } }
я нахожу typedefs совершенно необходимым для типобезопасного программирования, и его настоящий позор c# не имеет их встроенного. Разница между
void f(string connectionID, string username)доvoid f(ConID connectionID, UserName username)- это очевидно ...это может быть заманчиво использовать наследование, но это имеет некоторые основные ограничения:
- он не будет работать для примитивных типов
- производный тип все еще может быть приведен к исходному типу, т. е. мы можем отправить его в функцию, получающую наш исходный тип, это побеждает вся цель
- мы не можем наследовать от запечатанных классов (и т. е. многие классы .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) { var o = obj as SomeTypeTypeDef; return o is null ? false : Composed.Equals(o.Composed); } public bool Equals(SomeTypeTypeDefo) => object.Equals(this, o); // proxy the methods we want public void Method() => Composed.Method(); }хотя это будет работать, это очень многословно только для typedef. Кроме того, у нас есть проблема с сериализацией (т. е. в Json), поскольку мы хотим сериализовать класс через его составное свойство.
ниже приведен вспомогательный класс, который использует "Любопытно повторяющийся шаблон шаблона", чтобы сделать это намного проще:
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) { var o = obj as TDerived; return o is null ? false : 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будет сериализоваться в Json таким же образом, чтоSomeTypeделает.внимание, если мы реализуем
==и!=onSomeTypeTypeDefкомпилятор предупредит, чтоEqualsотсутствует, хотя он правильно наследуется, мы можем безопасно использовать:#pragma warning disable CS0660, CS0661отключить эти предупреждающий.
необходимость прокси-методов может быть хлопот, но также является благословением в маскировке, будучи новым типом, мы часто хотим только FW выбранные методы и добавить новые в наш "typedef"
надеюсь, что это помогает !
лучшая альтернатива
typedefчто я нашел в C#using. Например, я могу управлять точностью float с помощью флагов компилятора с помощью этого кода:#if REAL_T_IS_DOUBLE using real_t = System.Double; #else using real_t = System.Single; #endifк сожалению, это требует, чтобы вы разместили это в верхней части файл где вы используете
real_t. В настоящее время нет способа объявить глобальный тип пространства имен в C#.
Comments