Problem
Is there a typedef equivalent in C#, or a means to achieve something similar? I’ve done some research, but everything I find is negative. I’m now in a circumstance that looks like this:
class GenericClass<T>
{
public event EventHandler<EventData> MyEvent;
public class EventData : EventArgs { /* snip */ }
// ... snip
}
It doesn’t take a rocket scientist to figure out that trying to construct a handler for that event can soon lead to a lot of typing (apologies for the terrible pun). It would end up looking 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 I was already utilizing a complex type, not just an int, in my case. It would be wonderful if this could be made a little easier…
To get comparable behavior, consider typedefing the EventHandler rather to having to redefine it.
Asked by Matthew Scharley
Solution #1
No, there isn’t a genuine typedef equivalent. Within a single file, you can utilize ‘using’ directives, for example.
using CustomerList = System.Collections.Generic.List<Customer>;
However, this will only have an effect on the source file. In C and C++, typedef is commonly used within.h files that are extensively included, thus a single typedef can be used across an entire project. Because there is no #include mechanism in C# that allows you to include the using directives from one file in another, that ability does not exist.
Fortunately, there is a workaround in the example you provided: implicit method group conversion. You may just replace the event subscription line to:
gcInt.MyEvent += gcInt_MyEvent;
🙂
Answered by Jon Skeet
Solution #2
Jon really gave a nice solution, I didn’t know you could do that!
I had to resort to inheriting from the class and writing its constructors on occasion. E.g.
public class FooList : List<Foo> { ... }
It’s not the best solution (unless your assembly is utilized by others), but it gets the job done.
Answered by Jonathan C Dickinson
Solution #3
You can define a class with implicit operators to convert between the alias and the actual class if you know what you’re doing.
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 recommend it and have never used something like it, although it might work in some instances.
Answered by palswim
Solution #4
Both C++ and C# lack simple methods for creating new types that are semantically identical to existing types. Such ‘typedefs’ are absolutely necessary for type-safe programming in my opinion, and it’s a shame c# doesn’t have them built-in. It’s easy to see how void f(string connectionID, string username) differs from void f(ConID connectionID, UserName username).
(With boost in BOOST STRONG TYPEDEF, you can achieve something similar in C++.)
It may be tempting to employ inheritance, but there are some significant drawbacks:
In C#, the only option to achieve something comparable is to compose our type into a new class:
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 is functional, it is overly verbose for a typedef. We also have an issue serializing (i.e. to Json) the class because we wish to serialize it using its Composed property.
To make things easier, here’s a helper class that employs the “Curiously 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 is reduced to:
sealed Class SomeTypeTypeDef : Composer<SomeTypeTypeDef, SomeType> {
public SomeTypeTypeDef(SomeType composed) : base(composed) {}
// proxy the methods we want
public void Method() => Composed.Method();
}
Furthermore, the SomeTypeTypeDef will serialize to Json in the same manner as the SomeType.
I hope this information is useful!
Answered by kofifus
Solution #5
I don’t believe there is a typedef. In the GenericClass, you could only define a specific delegate type instead of the generic one, i.e.
public delegate GenericHandler EventHandler<EventData>
It would be shorter as a result of this. However, how about the following idea:
Make use of Visual Studio. As a result, when you typed
gcInt.MyEvent +=
Intellisense has already provided the whole event handler signature. It’s there if you press TAB. Accept or alter the produced handler name, then press TAB to auto-generate the handler stub.
Answered by OregonGhost
Post is based on https://stackoverflow.com/questions/161477/equivalent-of-typedef-in-c-sharp