399

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

13 Answers 13

414

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; 

:)

Sign up to request clarification or add additional context in comments.

10 Comments

I always forget that you can do this. Maybe because Visual Studio suggests the more verbose version. But I'm fine with pressing TAB twice instead of typing the handler name ;)
In my experience (which is scarce), you have to specify the fully qualified type name, for instance: using MyClassDictionary = System.Collections.Generic.Dictionary<System.String, MyNamespace.MyClass>; Is it correct? Otherwise it doesn't seem to consider the using definitions above it.
I couldn't convert typedef uint8 myuuid[16]; through "using" directive. using myuuid = Byte[16]; doesn't compile. using can be used just for creating type aliases. typedef seems to be much more flexible, since it can create an alias for a whole declaration (including array sizes). Is there any alternative in this case?
@natenho: Not really. The closest you could come would be to have a struct with a fixed-size buffer, probably.
@tunnuz Unless you specify it inside a namespace
|
46

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 Comments

Definitely a good method, but keep in mind that those (annoying) sealed types exist, and it won't work there. I really wish C# would introduce typedefs already. It's a desperate need (especially for C++ programmers).
I've created a project for this situation called LikeType which wraps the underlying type rather than inheriting from it. It will also implicitly convert TO the underlying type, so you could use something like public class FooList : LikeType<IReadOnlyList<Foo>> { ... } and then use it anywhere you would expect a IReadOnlyList<Foo>. My answer below shows more detail.
It also won't infer the type Foo if passed to e.g. template method that accepts List<T>. With proper typedef it would be possible.
23

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 Comments

Thanks @palswim, I got here looking for something like "typedef string Identifier;" so your suggestion may be just what I need.
I use this approach a lot in database systems so that while a CustomerId and OrderId are 32-bit integer values that would normally be Int32, they’re represented inside the program as distinct types so no-one will accidentally GetCustomerById( orderId ). That said, use a struct, not a class to avoid unnecessary heap allocations.
17

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 !

Comments

16

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.

2 Comments

This is not a typedef, since if you define an extension method that works on Bar it will also work on Foo. This is not what one would expect from a type-driven development perspective.
This can only define global type, but not replace a type inside a class to another.
6

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 Comments

Yea, that's what I did to generate the example. But coming back to look at it again AFTER the fact can still be confusing.
I know what you mean. That's why I like to keep my event signatures short, or get away from the FxCop recommendation to use Generic EventHandler<T> instead of my own delegate type. But then, stick with the short-hand version provided by Jon Skeet :)
If you've got ReSharper, it'll tell you that the long version is overkill (by colouring it in grey), and you can use a "quick fix" to get rid of it again.
6

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 Comments

I seriously need to getting around to having a good look at Linq... for the record though, I was building for 2.0 at the time (in VS 2008 though)
Oh, also, I can subscribe fine, but then to get at the event arguments I need an explicit cast, and preferably type checking code, just to be on the safe side.
The syntax is correct, but I wouldn't say it's "Linq syntax"; rather it is a lambda expression. Lambdas are a supporting feature that make Linq work, but are completely independent of it. Essentially, anywhere you can use a delegate, you can use a lambda expression.
Fair point, I should have said lambda. A delegate would work in .Net 2, but you'd need to explicitly declare the nested generic type again.
5

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 Comments

would LikeType work for something complex like stackoverflow.com/questions/50404586/… ? I've tried playing with it and can't get a class setup that works.
That's not really the intent of the LikeType library. LikeType's primary purpose is to help with Primitive Obsession, and as such, it doesn't want you to be able to pass around the wrapped type like it was the wrapper type. As in, if I make Age : LikeType<int> then if my function needs an Age, I want to ensure my callers are passing an Age, not an int.
That being said, I think I have an answer to your question, which I'll post over there.
5

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

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

Comments

5

I'd do

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

Comments

3

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 } 

1 Comment

Thx .. this was helpful in my case as Blazor keeps me from the use of "using ..."-Method shown above
1

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

Comments

1

Since the introduction of C# 10.0, we now have the global using directive.

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

This introduces CustomerList as alias of List<Customer> on a global scope (throughout the whole project and all references to it).

Though I would have liked to be able to limit its scope (say for instance 'internal using') this does actually do a terrific job of fulfilling a typedef variant in C#.

2 Comments

These can't exist in a namespace which kind of sucks.
For very very small values of "terrific".

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.