Proposal: Add support for typedef (like in C++) #1300
Replies: 61 comments 7 replies
-
You can do aliasing currently for types based on defines with use #if REAL_IS_DOUBLE
using real = System.Double;
#else
using real = System.Single;
#endif real Add(real a, real b)
{
return a + b;
} Or a native int type (assuming you set #if BIT64
using nint = System.Int64;
#else
using nint = System.Int32;
#endif |
Beta Was this translation helpful? Give feedback.
-
... and you have to repeat the alias definition in each source file where you want to use the alias. |
Beta Was this translation helpful? Give feedback.
-
I don't think it would be that hard for compiler to treat it as public, yet its not supported. |
Beta Was this translation helpful? Give feedback.
-
@MkazemAkhgary I would like a global class alias but it would probably be better to use a different keyword. |
Beta Was this translation helpful? Give feedback.
-
How about |
Beta Was this translation helpful? Give feedback.
-
@aaronfranke |
Beta Was this translation helpful? Give feedback.
-
It is useful as an abstract type because you can have situations where a data type can be either A or B, but in-file you just refer to it as type X. A single line can then be changed to make it become A or B. |
Beta Was this translation helpful? Give feedback.
-
I don't think this is a great way to factor code. I have no doubt it works well enough, but it is not expressive. A new language feature should enable clearer expression of ideas. |
Beta Was this translation helpful? Give feedback.
-
It seems like it should be possible at least to have assembly-scoped For me the use case would be more about wanting to avoid the syntactic overhead of putting IBlahBlahBlah everywhere, but not wanting to require clients to use a specific wrapper type. |
Beta Was this translation helpful? Give feedback.
-
@contextfree that's an excellent choice of syntax conveying the semantics perfectly. There's also a proposal, #1180, for namespace level method declarations. Such a feature would I think cover that proposal, which I quite like, as well: internal using static Namespace.Utilities;
namespace Namespace
{
static class Utilities
{
public static void WriteLine(object value) => Console.WriteLine(value);
}
} So while I dislike the example use case of this proposal, your suggestion would make it generalizable to much more interesting scenarios. |
Beta Was this translation helpful? Give feedback.
-
To extend this proposal with a more modern approach than typedefs: Haxe, a language for cross-compilation to other languages, has a concept called [abstract types] (https://haxe.org/manual/types-abstract.html) beside the typedefs . It allows you to define a type which is evaluated to its underlying type during compile time. For this type you can define custom operators, methods. If an abstract type cannot have additional members as the storage will be the defined underlying type, but it can have methods, properties and operators. Haxe also supports inlining of methods which might any overhead. If you do not inline the methods, there will be a class holding the methods as statics. This way you could even implement a "int" that can only hold even numbers. I personally do not like the fact that they call them "abstract types" as typedef Color : int
{
public inline Color(int i)
{
this = i;
}
public inline Color(Color c)
{
this = (int)c;
}
public inline byte Alpha => (this >> 24) & 0xFF;
public inline byte Red => (this >> 16) & 0xFF;
public inline byte Green => (this >> 8) & 0xFF;
public inline byte Blue => this & 0xFF;
public static inline Color FromArgb(byte a, byte r, byte g, byte b)
{
return new Color((a << 24) | (r << 16) | (g << 8) | b);
}
public static inline implicit operator int(Color c)
{
return c;
}
public static inline implicit operator Color(int i)
{
return new Color(i);
}
}
typedef GameNumber
#if BIT64
: double
#else
: float
#endif
{
public void DoSomething(GameEngine engine)
{
engine.PutToPool(this); // GameEngine.PutToPool(float f) or GameEngine.PutToPool(double d)
}
public static inline explicit operator long(Real c); // auto-operator via underlying type
public static inline implicit operator Real(long i); // auto-operator via underlying type
} There would be definitly some details to clarify like:
This would also allow a bit more strict typing within your library. |
Beta Was this translation helpful? Give feedback.
-
I'm not sure what that buys you on top of what you can already do today (just wrapping your value in a struct that has whatever API you want to expose). |
Beta Was this translation helpful? Give feedback.
-
The benefit in this case is that on compile time the type is reduced to the underlying type. This means it can also be a class or interface that you wrap in this case. When you wrap it into a struct you will have a lot of wrapping/unwrapping ongoing. Plus: You will have a new type in your assembly and public interface even though you just wanted to have a wrapper with some more meaning. My proposal is more an extension of a typedef too maybe allow more logic. Also a simple typedef could be rebuild with a struct/class + implicit cast operators but in the end that's not the goal. I saw especially in cross-platform compilation cases such types can be really useful especially if you have platform specific implementations. The bullet point with "exposing outside assemblies" is maybe misleading. By this I did not mean directly that the typedef is exposed and embedded in the assembly, but rather if you have typedefs in your contract, you might need to consider that the conversion operator of the passed value is invoked at the beginning of the method. If you have a |
Beta Was this translation helpful? Give feedback.
-
I missed your point the first few times I read this line. Meaning, you want to be able to pass your typedef type to things expecting ISomeInterface or SomeBaseClass? |
Beta Was this translation helpful? Give feedback.
-
Yes, you can basically wrap anything in the typedef, a class, interface, enum or struct (by this also standard data types like string). Just like in C++ the typedef becomes an alias for the original type. The simple version of a typedef could be The extended version brings a bit more strict typing into the game. At compile time the alias also gets replaced with the underlying type. But additionally you can define specific operators, methods and maybe properties. The code of those might either be inlined for performance reasons or be placed in a static class with extension methods. It covers the use case that the typedef is more than a simple alias for convenience but really a entity in your assembly it has some more semantics and should follow some rules. |
Beta Was this translation helpful? Give feedback.
-
I think the drawback here is that the portability/discoverability of the API consuming code is reduced. On Android, enums can be defined using a special TypeDef annotation syntax, which effectively allows enums to be treated as inline constants. In fact, this seems to be the preferred way to do things to save MB due to the horrendous size allocation for Java enums. |
Beta Was this translation helpful? Give feedback.
-
This would be nice to separate types of one type and different representation.
|
Beta Was this translation helpful? Give feedback.
-
Could result in more readable code (example simplified):
Of course you can create a class or struct, but that's more code and in some circumstances (like in internal code) it seems more reasonable to simplify it using a tuple instead of a class or struct. |
Beta Was this translation helpful? Give feedback.
-
Here's another thought: However this feature is implemented, it doesn't really need to be global, as long as it allows spanning many files. Perhaps a namespace-level This would be a more generic approach and could also allow things like |
Beta Was this translation helpful? Give feedback.
-
I have two use cases for this, both heavily performance-related:
Both of these could be mostly solved by allowing generics to take a restriction of "where T: Number" or similar (list the operations that must be supported, perhaps). Unfortunately this restriction is very difficult to enforce on the primitive types, as for complex reasons they're not first-class citizens in the type space. |
Beta Was this translation helpful? Give feedback.
-
It allows you to ensure you're not innappropriately mixing incorrect types. And, nicely, the runtime will effectively elide everything and you should have full double-perf. |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi , |
Beta Was this translation helpful? Give feedback.
-
Effectively yes. It will still be in the metadata signatures as Money, but that's a good thing.
It's just good inlining. The runtime will see that all your members are very simple, and this struct is just a simple wrapper around double. So effectively all jitting will elide everything unnecessary. |
Beta Was this translation helpful? Give feedback.
-
Much of this can be done with some
So you will see the default in code files as Then you can generate a new file which contains e.g. There is a new feature in the newest C# in preview with which this can be made much cleaner than what it gets to look like as described here, but even without code generation this can pretty easily be done with pre-build events ( |
Beta Was this translation helpful? Give feedback.
-
@ericwj The problem right now is that this definition has to be repeated in every file. There should be a project-level feature to include such kinds of definitions for usage in every file without having to copy it into each file and without setting up complicated build events (and if I'm understanding this, "number" would not show up as a valid type with intellisense if adding it was a pre-build event). |
Beta Was this translation helpful? Give feedback.
-
This has been championed, which means that the team will at least discuss it: #3428 |
Beta Was this translation helpful? Give feedback.
-
It will work with IntelliSense regardless of how you produce the file. It will just be plain C# that is part of the normal compilation. This might be useful for some people because
|
Beta Was this translation helpful? Give feedback.
-
Regarding the issue that it has to be repeated in every file, can this not be allowed to be included in the namespace directly? To avoid the repetition? |
Beta Was this translation helpful? Give feedback.
-
My usecase is wrapping a C library which uses |
Beta Was this translation helpful? Give feedback.
-
I'd like to have this and have the "typedef" exported. My use case: export "default" generic type instantiation without inheritance: export using Foo = Foo<Default>;
public struct Foo<T>
{
} I have used this kind of pattern many times for different reasons (making API consumption simpler for the typical use case being the most common, but also for creating testable code with DI through generics). However, today was the first time I wanted to use this for a struct, and I can't use inheritance there. |
Beta Was this translation helpful? Give feedback.
-
Currently, in C#, there is no
typedef
like in C++. My use case fortypedef
is writing math methods with simultaneous support forfloat
anddouble
(an abstract type, viatypedef
, would be used in place of either type). Or when creating a descriptive short-hand for types without objects.Would it be possible to add this functionality to the C# language spec?
Beta Was this translation helpful? Give feedback.
All reactions