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:

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...

Edit: ie. perhaps typedefing the EventHandler instead of needing to redefine it to get similar behaviour.

0

12 Answers

No, there's no true equivalent of typedef. You can use 'using' directives within one file, e.g.

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.

Fortunately, the example you give does have a fix - implicit method group conversion. You can change your event subscription line to just:

gcInt.MyEvent += gcInt_MyEvent; 

:)

9

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.

3

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.

2

Both C++ and C# are missing easy ways to create a new type 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 ...

(You can achieve something similar in C++ with boost in BOOST_STRONG_TYPEDEF)

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)

The only way to achieve a similar thing in C# is by composing our type in 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 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.

Below is a helper class that uses the "Curiously Recurring Template Pattern" to make this much simpler:

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:

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.

Hope this helps !

I think there is no typedef. You could only define a specific delegate type instead of the generic one in the GenericClass, i.e.

public delegate GenericHandler EventHandler<EventData> 

This would make it shorter. But what about the following suggestion:

Use Visual Studio. This way, when you typed

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.

3

C# supports some inherited covariance for event delegates, so a method like this:

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:

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

You can use an open source library and NuGet package called LikeType that I created that will give you the GenericClass<int> behavior that you're looking for.

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

With C# 10 you can now do

global using Bar = Foo 

Which works like a typedef within the project.

I haven't tested it in depth, so there might be quirks.

I'm using it like

global using DateTime = DontUseDateTime 

Where DontUseDateTime is a struct marked Obsolete, to force people to use NodaTime.

Here is the code for it, enjoy!, I picked that up from the dotNetReference type the "using" statement inside the namespace line 106

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

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:

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 } 

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:

#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 file where you use real_t. There is currently no way to declare a global namespace type in C#.

I'd do

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

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy