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。如果可以简化一点,那就太好了...
编辑:即。也许可以对EventHandler进行typedef定义,而不必重新定义它以获得类似的行为。
#1 楼
不,没有typedef的真正等效项。您可以在一个文件中使用'using'指令,例如using CustomerList = System.Collections.Generic.List<Customer>;
,但这只会影响该源文件。在C和C ++中,我的经验是
typedef
通常在广泛包含的.h文件中使用-因此单个typedef
可以在整个项目中使用。该功能在C#中不存在,因为C#中没有#include
功能,该功能可以让您将一个文件中的using
指令包含到另一个文件中。方法组转换。您可以将事件订阅行更改为:gcInt.MyEvent += gcInt_MyEvent;
:)
#2 楼
Jon确实提供了一个不错的解决方案,我不知道您能做到!有时我所求助的是从类继承并创建其构造函数。例如,
public class FooList : List<Foo> { ... }
不是最好的解决方案(除非您的程序集被他人使用),但是它可以工作。
评论
绝对是一个好方法,但是请记住,存在那些(讨厌的)密封类型,并且在那里不能使用。我真的希望C#已经引入typedef。这是一个迫切的需求(尤其是对于C ++程序员)。
– MasterMastic
2012年11月15日下午3:47
我针对这种情况创建了一个名为LikeType的项目,该项目包装了基础类型而不是从中继承。它还将隐式转换为基础类型,因此您可以使用公共类FooList之类的东西:LikeType
–马特·克莱因(Matt Klein)
16-3-16在19:01
如果传递给例如,它也不会推断出Foo类型。接受List
– Aleksei Petrenko
17年4月26日在17:57
#3 楼
如果您知道自己在做什么,则可以使用隐式运算符定义一个类,以在别名类和实际类之间进行转换。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,我在这里寻找“ typedef string Identifier”之类的东西。所以您的建议可能正是我所需要的。
– yoyo
13年5月31日在15:47
#4 楼
C ++和C#都缺少创建新类型的简便方法,该新类型在语义上与现有类型相同。我发现这样的'typedefs'对于类型安全的编程完全必要,而真正的遗憾是C#没有内置它们。void f(string connectionID, string username)
与void f(ConID connectionID, UserName username)
之间的区别是显而易见的... (您可以在BOOT_STRONG_TYPEDEF中使用boost提高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();
}
虽然可以工作,但对于typedef来说却非常冗长。 Json),因为我们想通过其Composed属性将其序列化。
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,上面的类变得简单起来: />希望这会有所帮助!
#5 楼
C#支持事件委托的某些继承的协方差,因此是这样的方法: br />void LowestCommonHander( object sender, EventArgs e ) { ... }
您甚至可以使用lambda语法,智能感知将全部为您完成:
gcInt.MyEvent += LowestCommonHander;
评论
我真的需要认真研究一下Linq ...虽然有记录,但我当时正在为2.0进行构建(尽管在VS 2008中)
–马修·沙利(Matthew Scharley)
08年10月2日在13:25
哦,我也可以订阅,但是为了安全起见,为了获得事件参数,我需要显式转换,最好是类型检查代码。
–马修·沙利(Matthew Scharley)
08年10月2日在13:26
语法是正确的,但我不会说它是“ Linq语法”。而是一个lambda表达式。 Lambda是使Linq正常工作的支持功能,但完全独立于它。本质上,在任何可以使用委托的地方,都可以使用lambda表达式。
–斯科特·多曼(Scott Dorman)
08年10月2日在14:15
公平点,我应该说lambda。委托可以在.Net 2中使用,但是您需要再次显式声明嵌套的泛型类型。
–基思
08年10月2日在16:38
#6 楼
我认为没有typedef。您只能定义特定的委托类型,而不能定义GenericClass中的泛型类型,即但是以下建议呢?
使用Visual Studio。这样,当您键入
public delegate GenericHandler EventHandler<EventData>
时,它已经提供了Intellisense的完整事件处理程序签名。按TAB即可。接受或更改生成的处理程序名称,然后再次按TAB键以自动生成处理程序存根。
评论
是的,这就是我生成示例的过程。但是,事实再次出现后,再来看看它仍然会造成混乱。
–马修·沙利(Matthew Scharley)
08年10月2日在9:25
我明白你的意思。这就是为什么我希望保持事件签名简短,或者不使用FxCop建议使用Generic EventHandler
–俄勒冈州幽灵
08年10月2日在9:28
如果您拥有ReSharper,它会告诉您长版本过大(通过将其涂成灰色),并且可以使用“快速修复”来再次删除它。
–罗杰·利普斯科姆
08-10-2在9:38
#7 楼
您可以使用我创建的一个开源库和一个名为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));
}
}
评论
LikeType是否可以用于诸如stackoverflow.com/questions/50404586/…之类的复杂事物?我尝试过使用它,但无法获得有效的课程设置。
–杰伊·克罗汉(Jay Croghan)
18年5月18日在7:28
这不是LikeType库的真正目的。 LikeType的主要目的是帮助原始痴迷,因此,它不希望您能够像包装类型那样传递包装类型。如图所示,如果我使用Age:LikeType
–马特·克莱因(Matt Klein)
18年5月18日在14:37
话虽如此,我想我对你的问题有一个答案,我将在那儿发布。
–马特·克莱因(Matt Klein)
18年5月18日在14:38
#8 楼
这是它的代码,请尽情享受! /win32/win32native.csusing 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"};
}
}
}
#9 楼
对于非密封的类,只需从它们继承即可:public class Vector : List<int> { }
#10 楼
我在C#中找到的typedef
的最佳替代品是using
。例如,我可以使用以下代码通过编译器标志控制浮点精度:不幸的是,它要求将其放置在每个使用real_t
的文件的顶部。 。当前无法在C#中声明全局名称空间类型。
评论
我总是忘记了你可以做到这一点。可能是因为Visual Studio建议使用更详细的版本。但是我可以按两次TAB而不是键入处理程序名称;)
–俄勒冈州幽灵
08年10月2日在9:25
以我的经验(很少),您必须指定完全限定的类型名称,例如:using MyClassDictionary = System.Collections.Generic.Dictionary
–tunnuz
10-4-23在10:04
我无法转换typedef uint8 myuuid [16];通过“使用”指令。使用myuuid = Byte [16];不编译。 using只能用于创建类型别名。 typedef似乎更加灵活,因为它可以为整个声明(包括数组大小)创建别名。在这种情况下还有其他选择吗?
–natenho
14年8月26日在18:09
@natenho:不是。您可能最接近的可能是具有固定大小缓冲区的结构。
–乔恩·斯基特(Jon Skeet)
14年8月26日在18:10
@tunnuz,除非您在名称空间中指定
–约翰·史密斯
17年1月8日在13:12