相当于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型。如果能简化一下就好了……

编辑:ie。也许是对EventHandler进行类型定义,而不需要重新定义它来获得类似的行为。

249735 次浏览

不,没有typedef的真正对等物。你可以在一个文件中使用“using”指令,例如:

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

但这只会影响源文件。在C和c++中,我的经验是typedef通常在广泛包含的.h文件中使用——因此一个typedef可以在整个项目中使用。这种能力在c#中不存在,因为c#中没有允许你将一个文件中的using指令包含在另一个文件中的#include功能。

幸运的是,你给出的例子有一个固定隐式方法组转换。您可以更改您的事件订阅行:

gcInt.MyEvent += gcInt_MyEvent;

:)

我认为没有类型定义。你只能在GenericClass中定义一个特定的委托类型,而不是泛型委托类型。

public delegate GenericHandler EventHandler<EventData>

这会使它更短。但是下面的建议怎么样:

使用Visual Studio。这样,当你打字的时候

gcInt.MyEvent +=

它已经从智能感知中提供了完整的事件处理程序签名。按TAB键,它就在那里。接受生成的处理程序名称或更改它,然后再次按TAB键自动生成处理程序存根。

Jon给出了一个很好的解决方案,我不知道你能做到!

有时我要做的是从类继承并创建它的构造函数。如。

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

不是最好的解决方案(除非您的程序集被其他人使用),但它是有效的。

c#支持一些事件委托的继承协方差,所以像这样的方法:

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

可以用来订阅您的事件,没有显式强制转换所需

gcInt.MyEvent += LowestCommonHander;

你甚至可以使用lambda语法,智能感知将为你完成:

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

如果您知道自己在做什么,那么可以定义一个类,使用隐式操作符在别名类和实际类之间进行转换。

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

我并不赞同这种方法,也从未使用过类似的方法,但这可能适用于某些特定的情况。

你可以使用我创建的一个名为LikeType的开源库和NuGet包,它会给你你正在寻找的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中得到的 在命名空间第106行中键入“using”语句 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"};
}
}
}

我在c#中发现的typedef的最佳替代品是using。例如,我可以用下面的代码通过编译器标志来控制浮点精度:

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

不幸的是,它要求你把它放在你使用real_t每个文件的顶部。目前没有办法在c#中声明全局命名空间类型。

c++和c#都缺少创建与现有类型在语义上相同的类型的简单方法。我发现这样的“typedefs”对于类型安全编程来说是完全必要的,c#没有内置它们真是太遗憾了。void f(string connectionID, string username)void f(ConID connectionID, UserName username)之间的区别很明显…

(你可以在c++中通过BOOST_STRONG_TYPEDEF中的boost实现类似的功能)

使用继承可能很诱人,但它有一些主要的局限性:

  • 它不适用于基本类型
  • 派生类型仍然可以被转换为原始类型,即我们可以将它发送给接收原始类型的函数,这违背了整个目的
  • 我们不能从密封类中派生(即许多.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();
}

虽然这将工作,这是非常冗长的只是一个类型定义。 此外,我们还有一个序列化(即Json)的问题,因为我们想通过它的compose属性序列化类

下面是一个使用“奇怪的重复模板模式”的帮助类。让事情变得更简单:

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


}

使用Composer,上面的类变得简单:

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


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

此外,SomeTypeTypeDef将以与SomeType相同的方式序列化为Json。

希望这能有所帮助!

对于非密封类,只需从它们继承:

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
}

在c# 10中,你可以这样做

global using Bar = Foo

它的工作方式类似于项目中的类型定义。

我还没有对它进行深入测试,所以可能会有一些怪癖。

我用它就像

global using DateTime = DontUseDateTime

其中DontUseDateTime是一个标记为Obsolete的结构体,以强制人们使用NodaTime。

我会做

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

自从引入c# 10.0以来,我们现在有了global using指令。

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

这将在全局作用域中引入CustomerList作为List<Customer>的别名(贯穿整个项目和对它的所有引用)。

虽然我希望能够限制它的范围(例如“内部使用”),但这实际上在c#中实现类型定义变量方面做得很好。