C#中typedef的等价物

Mat*_*ley 308 c# typedef

在C#中是否有typedef等价物,或者某种类似的行为?我做了一些谷歌搜索,但我看到的每个地方似乎都是负面的.目前我的情况类似于以下情况:

class GenericClass<T> 
{
    public event EventHandler<EventData> MyEvent;
    public class EventData : EventArgs { /* snip */ }
    // ... snip
}
Run Code Online (Sandbox Code Playgroud)

现在,当试图为该事件实现处理程序时,并不需要火箭科学家弄清楚这会很快导致大量输入(为可怕的双关语道歉).它最终会像这样:

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();
}
Run Code Online (Sandbox Code Playgroud)

除了在我的情况下,我已经使用了复杂类型,而不仅仅是一个int.如果有可能简化这一点,那就太好了......

编辑:即.也许键入定义EventHandler而不是需要重新定义它以获得类似的行为.

Jon*_*eet 325

不,没有类似typedef的真正等价物.您可以在一个文件中使用'using'指令,例如

using CustomerList = System.Collections.Generic.List<Customer>;
Run Code Online (Sandbox Code Playgroud)

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

幸运的是,您给出的示例确实有一个修复 - 隐式方法组转换.您可以将您的活动订阅行更改为:

gcInt.MyEvent += gcInt_MyEvent;
Run Code Online (Sandbox Code Playgroud)

:)

  • 我总是忘记你可以做到这一点.也许是因为Visual Studio建议使用更详细的版本.但我按两次T​​AB而不是输入处理程序名称就可以了;) (11认同)
  • 根据我的经验(很少),你必须指定完全限定的类型名称,例如:`using MyClassDictionary = System.Collections.Generic.Dictionary <System.String,MyNamespace.MyClass>;`它是否正确?否则它似乎不考虑它上面的`using`定义. (11认同)
  • 我无法转换`typedef uint8 myuuid [16];`通过'使用"指令.`使用myuuid = Byte [16];`不编译.`using`只能用于创建**类**别名.`typedef`似乎更灵活,因为它可以为整个声明(包括数组大小)创建别名.在这种情况下还有其他选择吗? (3认同)
  • @tunnuz:是的,你是对的 - 我会更新答案. (2认同)
  • @natenho:不是.你最接近的可能是有一个带固定大小缓冲区的结构. (2认同)

Jon*_*son 36

乔恩真的给了一个很好的解决方案,我不知道你能做到这一点!

我使用的有时候是从类继承并创建它的构造函数.例如

public class FooList : List<Foo> { ... }
Run Code Online (Sandbox Code Playgroud)

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

  • 绝对是一个很好的方法,但请记住,那些(烦人的)密封类型存在,并且它不会在那里工作.我真的希望C#已经引入了typedef.这是一种迫切的需求(特别是对于C++程序员). (41认同)
  • 如果传递给接受`List <T>`的模板方法,它也不会推断类型`Foo`.使用正确的typedef就可以了. (3认同)

pal*_*wim 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 };
    }
}
Run Code Online (Sandbox Code Playgroud)

我实际上并不赞同这一点,并且从未使用过类似的东西,但这可能适用于某些特定情况.


kof*_*fus 12

C++ 和 C# 都缺少创建在语义上与现有类型相同的类型的简单方法。我发现这样的“typedefs”对于类型安全编程完全必要,而且 c# 没有内置它们真的很遗憾。void f(string connectionID, string username)to之间的区别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();
}
Run Code Online (Sandbox Code Playgroud)

虽然这会起作用,但对于 typedef 来说是非常冗长的。此外,我们在序列化(即到 Json)方面存在问题,因为我们想通过其 Composed 属性序列化类。

下面是一个帮助类,它使用“Curious Recurring Template Pattern”使这更简单:

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

   // proxy the methods we want
   public void Method() => Composed.Method();
}
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助 !


JJJ*_*JJJ 12

使用 C# 10,您现在可以执行以下操作

global using Bar = Foo
Run Code Online (Sandbox Code Playgroud)

它的工作原理类似于项目中的 typedef。

我还没有深入测试过,所以可能会有一些奇怪的地方。

我用它就像

global using DateTime = DontUseDateTime
Run Code Online (Sandbox Code Playgroud)

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


Kei*_*ith 6

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

void LowestCommonHander( object sender, EventArgs e ) { ... } 
Run Code Online (Sandbox Code Playgroud)

可用于订阅您的活动,无需显式转换

gcInt.MyEvent += LowestCommonHander;
Run Code Online (Sandbox Code Playgroud)

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

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

  • 语法是正确的,但我不会说它是"Linq语法"; 相反,它是一个lambda表达式.Lambdas是一个支持功能,使Linq工作,但完全独立于它.基本上,在任何可以使用委托的地方,您都可以使用lambda表达式. (9认同)

Ore*_*ost 5

我认为没有typedef.您只能定义特定的委托类型,而不是GenericClass中的通用委托类型,即

public delegate GenericHandler EventHandler<EventData>
Run Code Online (Sandbox Code Playgroud)

这会缩短它.但是以下建议呢:

使用Visual Studio.这样,当你输入

gcInt.MyEvent += 
Run Code Online (Sandbox Code Playgroud)

它已经提供了Intellisense的完整事件处理程序签名.按TAB,就在那里.接受生成的处理程序名称或更改它,然后再次按TAB以自动生成处理程序存根.

  • 是的,这就是我为生成这个例子所做的.但回过头来再看一遍这个事实仍然会令人困惑. (2认同)
  • 如果你有ReSharper,它会告诉你长版本是过度的(用灰色着色),你可以使用"快速修复"来再次摆脱它. (2认同)

Mat*_*ein 5

您可以使用我创建的名为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));
    }
}
Run Code Online (Sandbox Code Playgroud)


sha*_*m02 5

这是它的代码,享受吧!我从 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"};
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


OS *_*eak 5

我会做

using System.Collections.Generic;
global using CustomerList = List<Customer>;
Run Code Online (Sandbox Code Playgroud)