相当于 C# 中的 typedef
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/161477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Equivalent of typedef in C#
提问by Matthew Scharley
Is there a typedef equivalent in C#, or someway to get some sort of similar behaviour? I've done some googling, but everywhere I look seems to be negative. Currently I have a situation similar to the following:
C# 中是否有 typedef 等价物,或者以某种方式获得某种类似的行为?我已经做了一些谷歌搜索,但我所看到的任何地方似乎都是负面的。目前我有类似以下的情况:
class GenericClass<T>
{
public event EventHandler<EventData> MyEvent;
public class EventData : EventArgs { /* snip */ }
// ... snip
}
Now, it doesn't take a rocket scientist to figure out that this can very quickly lead to a lot of typing (apologies for the horrible pun) when trying to implement a handler for that event. It'd end up being something like this:
现在,当试图为该事件实现处理程序时,不需要火箭科学家就能弄清楚这会很快导致大量输入(对可怕的双关语表示歉意)。它最终会是这样的:
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();
}
Except, in my case, I was already using a complex type, not just an int. It'd be nice if it were possible to simplify this a little...
除了,就我而言,我已经在使用复杂类型,而不仅仅是 int。如果可以稍微简化一下就太好了......
Edit: ie. perhaps typedefing the EventHandler instead of needing to redefine it to get similar behaviour.
编辑:即。也许 typedefing EventHandler 而不是需要重新定义它以获得类似的行为。
采纳答案by Jon Skeet
No, there's no true equivalent of typedef. You can use 'using' directives within one file, e.g.
不,没有真正的 typedef 等价物。您可以在一个文件中使用“使用”指令,例如
using CustomerList = System.Collections.Generic.List<Customer>;
but that will only impact that source file. In C and C++, my experience is that typedef
is usually used within .h files which are included widely - so a single typedef
can be used over a whole project. That ability does not exist in C#, because there's no #include
functionality in C# that would allow you to include the using
directives from one file in another.
但这只会影响该源文件。在 C 和 C++ 中,我的经验是它typedef
通常在广泛包含的 .h 文件中使用 - 因此typedef
可以在整个项目中使用单个文件。这种能力在 C# 中不存在,因为 C# 中没有#include
允许您将using
一个文件中的指令包含在另一个文件中的功能。
Fortunately, the example you give doeshave a fix - implicit method group conversion. You can change your event subscription line to just:
幸运的是,您给出的示例确实有一个修复 - 隐式方法组转换。您可以将您的活动订阅行更改为:
gcInt.MyEvent += gcInt_MyEvent;
:)
:)
回答by OregonGhost
I think there is no typedef. You could only define a specific delegate type instead of the generic one in the GenericClass, i.e.
我认为没有typedef。你只能在 GenericClass 中定义一个特定的委托类型而不是通用的类型,即
public delegate GenericHandler EventHandler<EventData>
This would make it shorter. But what about the following suggestion:
这将使它更短。但是下面的建议呢:
Use Visual Studio. This way, when you typed
使用 Visual Studio。这样,当你输入
gcInt.MyEvent +=
it already provides the complete event handler signature from Intellisense. Press TAB and it's there. Accept the generated handler name or change it, and then press TAB again to auto-generate the handler stub.
它已经提供了来自 Intellisense 的完整事件处理程序签名。按TAB,它就在那里。接受生成的处理程序名称或更改它,然后再次按 TAB 以自动生成处理程序存根。
回答by Jonathan C Dickinson
Jon really gave a nice solution, I didn't know you could do that!
乔恩真的给出了一个很好的解决方案,我不知道你能做到!
At times what I resorted to was inheriting from the class and creating its constructors. E.g.
有时我采取的是从类继承并创建它的构造函数。例如
public class FooList : List<Foo> { ... }
Not the best solution (unless your assembly gets used by other people), but it works.
不是最好的解决方案(除非您的程序集被其他人使用),但它有效。
回答by Keith
C# supports some inherited covariance for event delegates, so a method like this:
C# 支持事件委托的一些继承协方差,所以有这样的方法:
void LowestCommonHander( object sender, EventArgs e ) { ... }
Can be used to subscribe to your event, no explicit cast required
可用于订阅您的事件,无需显式转换
gcInt.MyEvent += LowestCommonHander;
You can even use lambda syntax and the intellisense will all be done for you:
您甚至可以使用 lambda 语法,智能感知将为您完成:
gcInt.MyEvent += (sender, e) =>
{
e. //you'll get correct intellisense here
};
回答by palswim
If you know what you're doing, you can define a class with implicit operators to convert between the alias class and the actual class.
如果您知道自己在做什么,就可以定义一个带有隐式运算符的类,以在别名类和实际类之间进行转换。
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 };
}
}
I don't actually endorse this and haven't ever used something like this, but this could probably work for some specific circumstances.
我实际上并不认可这一点,也从未使用过这样的东西,但这可能适用于某些特定情况。
回答by Matt Klein
You can use an open source library and NuGet package called LikeTypethat I created that will give you the GenericClass<int>
behavior that you're looking for.
您可以使用我创建的名为LikeType 的开源库和 NuGet 包,它们将为您提供所需的GenericClass<int>
行为。
The code would look like:
代码如下所示:
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));
}
}
回答by shakram02
Here is the code for it, enjoy!, I picked that up from the dotNetReference type the "using" statement inside the namespace line 106 http://referencesource.microsoft.com/#mscorlib/microsoft/win32/win32native.cs
这是它的代码,享受!,我从 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"};
}
}
}
回答by Aaron Franke
The best alternative to typedef
that I've found in C# is using
. For example, I can control float precision via compiler flags with this code:
typedef
我在 C# 中找到的最好的替代方案是using
. 例如,我可以使用以下代码通过编译器标志控制浮点精度:
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
Unfortunately, it requires that you place this at the top of every filewhere you use real_t
. There is currently no way to declare a global namespace type in C#.
不幸的是,它要求你把它放在你使用的每个文件的顶部real_t
。目前无法在 C# 中声明全局命名空间类型。
回答by kofifus
Both C++ and C# are missing easy ways to create a newtype which is semantically identical to an exisiting type. I find such 'typedefs' totally essential for type-safe programming and its a real shame c# doesn't have them built-in. The difference between void f(string connectionID, string username)
to void f(ConID connectionID, UserName username)
is obvious ...
C++ 和 C# 都缺少创建在语义上与现有类型相同的新类型的简单方法。我发现这样的“typedefs”对于类型安全编程完全必要,而且 c# 没有内置它们真的很遗憾。void f(string connectionID, string username)
to之间的区别void f(ConID connectionID, UserName username)
很明显...
(You can achieve something similar in C++ with boost in BOOST_STRONG_TYPEDEF)
(你可以在 C++ 中通过 BOOST_STRONG_TYPEDEF 的 boost 实现类似的东西)
It may be tempting to use inheritance but that has some major limitations:
使用继承可能很诱人,但这有一些主要限制:
- it will not work for primitive types
- the derived type can still be casted to the original type, ie we can send it to a function receiving our original type, this defeats the whole purpose
- we cannot derive from sealed classes (and ie many .NET classes are sealed)
- 它不适用于原始类型
- 派生类型仍然可以转换为原始类型,即我们可以将其发送到接收原始类型的函数,这违背了整个目的
- 我们不能从密封类派生(即许多 .NET 类是密封的)
The only way to achieve a similar thing in C# is by composing our type in a new class:
在 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();
}
While this will work it is very verbose for just a typedef. In addition we have a problem with serializing (ie to Json) as we want to serialize the class through its Composed property.
虽然这会起作用,但对于 typedef 来说是非常冗长的。此外,我们在序列化(即到 Json)方面存在问题,因为我们想通过其 Composed 属性序列化类。
Below is a helper class that uses the "Curiously Recurring Template Pattern" to make this much simpler:
下面是一个帮助类,它使用“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);
}
}
}
With Composer the above class becomes simply:
使用 Composer,上面的类变得很简单:
sealed Class SomeTypeTypeDef : Composer<SomeTypeTypeDef, SomeType> {
public SomeTypeTypeDef(SomeType composed) : base(composed) {}
// proxy the methods we want
public void Method() => Composed.Method();
}
And in addition the SomeTypeTypeDef
will serialize to Json in the same way that SomeType
does.
此外,SomeTypeTypeDef
将以同样的方式序列化为 Json SomeType
。
Hope this helps !
希望这可以帮助 !
回答by Vlad Rudenko
For non-sealed classes simply inherit from them:
对于非密封类,只需从它们继承:
public class Vector : List<int> { }
But for sealed classes it's possible to simulate typedef behavior with such base class:
但是对于密封类,可以使用这样的基类模拟 typedef 行为:
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
}