Type alias in C#

  • Thread starter peter.tornqvist
  • Start date
P

peter.tornqvist

Maybe I'm stupid or maybe I am missing the obvious, but I can't find a way
to define a type alias in C# for primitive types (class and interfaces I can
inherit from, no problem). I.e I want to declare a type that is an alias
for, say, int. Conceptually, this is what I want to do:

public MyIntType = int; // won't compile

Anyone knows how to do this?
 
M

Marc Gravell

On a class-file -by- class-file basis you can use (at the top)

using MyIntType = System.Int32;
I.e I want to declare a type that is an alias for, say, int

Well, which do you want to declare: a type or an alias? They are mutually
exclusive. You can't subclass a struct, however.

Marc
 
P

peter.tornqvist

Well, which do you want to declare: a type or an alias? They are mutually
exclusive. You can't subclass a struct, however.

What is the difference in C#? In Delphi (where I have the most experience),
you create an alias by doing:

type
MyIntType = integer;

and a new type deriving from another type with:

type
MyIntType = type integer;

I'd be interested knowing the different options available in C#
 
M

Marc Gravell

Well, in the "class" case, the two syntax demos would be

using SomeAlias = Full.Namespace.To.BaseClass;

vs.

class SomeClass : BaseClass {}

A: The first (alias) is purely a compiler trick *within a single source
file*, to allow two things:
1: (more commonly) to avoid name conflicts within a file without having to
use fully qualified names, e.g. you have a custom class called (bizarrely)
XmlDocument, and you are using System.Xml, so you might alias one of the two
(or both), e.g.
using MyXmlDocument = My.Namespace.XmlDocument;

this now means that in your class file you can use
MyXmlDocument doc = new MyXmlDocument();
etc without the risk of conflicts, *but* the compiled IL only knows about
My.Namespace.XmlDocument.

2: (less common) to allow the programmer to change the backing type.
Sometimes generics can help, but note that generics do not respect
arithmetic (or other) operators. So if you weren't sure if you needed long
or int, you could use
using SomeAlias = System.Int32;
and then
SomeAlias value = 0;
value += 5;
etc
And if you change your mind later you can change just the alias to Int64.
*If* all the operations are still supported, then it will compile and work,
(but may break your interfaces).

B: the second (inheritance) defines a new type in your assembly inherited
from the old. This will allow automatic casting in one direction only, and
*possibly* won't change the behaviour significantly, but is not a good idea.
As well as being messy, it won't work with interface, static, sealed,
struct, etc.

Marc
 
C

Christopher Ireland

Marc Gravell said:
B: the second (inheritance) defines a new type in your assembly inherited
from the old. This will allow automatic casting in one direction only, and
*possibly* won't change the behaviour significantly, but is not a good
idea. As well as being messy, it won't work with interface, static,
sealed, struct, etc.

It also won't work with value types (System.Double, System.Int, System.Array
etc.) as they cannot be inherited from. The OP was talking about the [very
nice] Delphi language feature called 'type identity' which means the ability
to create a completely new value type based on one of the existing value
types. AFAIK, this can't be done in C#; you're stuck with using only the
predefined value types.

Chris.
 
M

Marc Gravell

He asked what the options were; I thought I laid out the options, and yes:
you are right: none of them include what he wants to do. But I never implied
that they would.

Out of interest, what (in Delphi) does this give you? It's been quite a
while since my last object-pascal days... can you add methods etc? If so,
then (looking to the future) possibly the C# 3.0 extension functions may be
of interest. But no use today.
It also won't work with value types...
Hence "struct" in that list.

Perhaps the "other other" alternative is to declare a struct that
encapsulates the required type, and provide an implicit cast operator in
both directions, plus any other operators etc that are needed (oh for some
static inheritance ;-p). A bit more messing than the OP would no-doubt want
(coming from the Delphi route), but achievable. I've done it many times.

Oh, and System.Array is a reference type.

Marc
 
C

Carl Daniel [VC++ MVP]

Marc said:
He asked what the options were; I thought I laid out the options, and
yes: you are right: none of them include what he wants to do. But I
never implied that they would.

Out of interest, what (in Delphi) does this give you? It's been quite
a while since my last object-pascal days... can you add methods etc?
If so, then (looking to the future) possibly the C# 3.0 extension
functions may be of interest. But no use today.

In a nutshell, it gives you the same thing that having Enums be distinct
types gives you - better error detection at compile time since mixing values
and variables from different domains without coersion will cause errors.

-cd
 
P

Peter Thornqvist

Thanks, Marc and Cristopher.

It's funny how MS has gone from typedef'ing everything in sight in C/C++ to
creating a language that doesn't allow it at all. This isn't critical to me,
but it would have been nice to be able to define something like

public Hwnd = IntPtr;

to make it more obvious what is expected when declaring method parameters
and interop signatures.
 
M

Marc Gravell

Cheers - good explanation

[drifts back over projects past... ahh... now I remember ;-p]

Marc
 
C

Christopher Ireland

Marc Gravell said:
He asked what the options were; I thought I laid out the options, and yes:
you are right: none of them include what he wants to do. But I never
implied that they would.
OK.

Out of interest, what (in Delphi) does this give you?

The ability to easily define your own value types.
It's been quite a while since my last object-pascal days... can you add
methods etc?

Not using that particular syntax route, no.
If so, then (looking to the future) possibly the C# 3.0 extension
functions may be of interest. But no use today.
Possibly.

Hence "struct" in that list.

Not all value types are structures, for example, enum.
Perhaps the "other other" alternative is to declare a struct that
encapsulates the required type, and provide an implicit cast operator in
both directions, plus any other operators etc that are needed (oh for some
static inheritance ;-p). A bit more messing than the OP would no-doubt
want (coming from the Delphi route), but achievable. I've done it many
times.

Sure you can. Hey, we could all program in assembly and notepad ;-)
Oh, and System.Array is a reference type.

Yup, you're right. You can't inherit from it though.

Chris.
 
M

Marc Gravell

I never said it was a /fantastic/ option - indeed I only added it as an
afterthought.

But it can be made to work... if the desire is strong enough.

Happy coding ;-p

Marc
 
D

Dustin Campbell

Thanks, Marc and Cristopher.
It's funny how MS has gone from typedef'ing everything in sight in
C/C++ to creating a language that doesn't allow it at all. This isn't
critical to me, but it would have been nice to be able to define
something like

public Hwnd = IntPtr;

to make it more obvious what is expected when declaring method
parameters and interop signatures.

Peter,

Marc mentioned a trick that I've used successfully in the past. Create a
new struct called "Hwnd" that is implicitly-convertible to IntPtr by overloading
the implicit-conversion operators. This has been especially useful because
methods can be added to the Hwnd struct that hide underlying P/Invoke methods.
Here's a basic example:

public struct HWnd: IWin32Window
{
// private fields...
private IntPtr m_Handle;

// constructors...
public HWnd(IntPtr handle)
{
return m_Handle = handle;
}

// P/Invoke methods...
[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, [Out] StringBuilder
lpClassName, int nMaxCount);

// public methods...
public string GetClassName()
{
const int MAX_LENGTH = 256;
StringBuilder classNameBuilder = new StringBuilder(MAX_LENGTH);
int count = GetClassName(m_Handle, classNameBuilder, MAX_LENGTH);
return classNameBuilder.ToString(0, count);
}

// IWin32Window properties...
public IntPtr Handle { get { return m_Handle; } }

// operators...
public static implicit operator IntPtr(HWnd hwnd)
{
return hwnd.Handle;
}
public static implict operator HWnd(IntPtr ptr)
{
return new HWnd(ptr);
}
}

Admittedly, there's a bit of extra code but it is a workable solution.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
C

Christopher Ireland

Marc Gravell said:
I never said it was a /fantastic/ option - indeed I only added it as an
afterthought.
OK

But it can be made to work... if the desire is strong enough.

Yes indeed.
Happy coding ;-p

Thank you, you too!

Chris.
 
P

Peter Thornqvist

Marc mentioned a trick that I've used successfully in the past. Create a
new struct called "Hwnd" that is implicitly-convertible to IntPtr by
overloading the implicit-conversion operators. This has been especially
useful because methods can be added to the Hwnd struct that hide
underlying P/Invoke methods.

That is indeed very powerful. Will the HWnd struct be properly
wrapped/unwrapped if I used it as parameter in an interop call, i.e if I did
this in another class (not in the HWnd struct):
public struct HWnd: IWin32Window
{
....
}

public class Win32{

[DllImport("user32.dll")]
private static extern int GetClassName(HWnd hWnd, [Out] StringBuilder
lpClassName, int nMaxCount);
}

would the HWnd parameter be correctly interpreted? Is that what the implicit
operator declarations does or is there something else as play here?
 
D

Dustin Campbell

Marc mentioned a trick that I've used successfully in the past.
Create a new struct called "Hwnd" that is implicitly-convertible to
IntPtr by overloading the implicit-conversion operators. This has
been especially useful because methods can be added to the Hwnd
struct that hide underlying P/Invoke methods.
That is indeed very powerful. Will the HWnd struct be properly
wrapped/unwrapped if I used it as parameter in an interop call, i.e if
I did
this in another class (not in the HWnd struct):
public struct HWnd: IWin32Window
{
...
}
public class Win32{

[DllImport("user32.dll")]
private static extern int GetClassName(HWnd hWnd, [Out]
StringBuilder
lpClassName, int nMaxCount);
}

would the HWnd parameter be correctly interpreted? Is that what the
implicit operator declarations does or is there something else as play
here?


Yes, the HWnd parameter would be interpreted correctly but I'd be careful
using it in P/Invoke calls because there is always the potential that you
could change the struct later in a way that breaks the call in odd ways (for
example, if you added another field to support other code in the struct).

Here's a test console app to try:

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;

namespace ConsoleTest
{
class Program
{
[STAThread]
static void Main(string[] args)
{
Form form = new Form();
HWnd formHandle = form.Handle;
Console.WriteLine(GetClassName(formHandle));
}

// P/Invoke methods...
[DllImport("user32.dll")]
private static extern int GetClassName(HWnd hWnd, [Out] StringBuilder
lpClassName, int nMaxCount);

// public methods...
public static string GetClassName(HWnd hWnd)
{
const int MAX_LENGTH = 256;
StringBuilder classNameBuilder = new StringBuilder(MAX_LENGTH);
int count = GetClassName(hWnd, classNameBuilder, MAX_LENGTH);
if (count == 0)
throw new Win32Exception();

return classNameBuilder.ToString(0, count);
}
}

public struct HWnd: IWin32Window
{
// private fields...
private IntPtr m_Handle;
private bool m_Value;

// constructors...
public HWnd(IntPtr handle)
{
m_Handle = handle;
m_Value = true;
}

// IWin32Window properties...
public IntPtr Handle { get { return m_Handle; } }

// operators...
public static implicit operator IntPtr(HWnd hwnd)
{
return hwnd.Handle;
}
public static implicit operator HWnd(IntPtr ptr)
{
return new HWnd(ptr);
}
}
}

This will fail. However, if you remove the "m_Value" field from the HWnd
struct, it works fine.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
D

Dave Sexton

Hi Christopher,
Not all value types are structures, for example, enum.

All value-types are structs. Enum is a struct as well.

Anything that derives from System.ValueType is a value-type. When you declare
a struct in C#, the compiler will ensure that it derives from
System.ValueType.

System.Enum derives from System.ValueType.

Certain classes may expose value-type semantics, such as System.String or
custom classes that override Equals, but they are certainly not true
value-types in terms of managed memory.
 
C

Christopher Ireland

Dave Sexton said:
Hi Christopher,


All value-types are structs. Enum is a struct as well.

I'm not so sure. Lutz Roeder's refector declares Enum as a 'public abstract
class' and the System.Type class has a specific 'IsEnum' property, the only
listed value type to have its own specific 'IsX' property. It may not a be a
'class' (IsClass returns false) as such, but I'm not convinced that it's a
struct either.
Anything that derives from System.ValueType is a value-type. When you
declare a struct in C#, the compiler will ensure that it derives from
System.ValueType.

True. All structures are value types.
System.Enum derives from System.ValueType.

Which does not necessarily make it a structure.
Certain classes may expose value-type semantics, such as System.String or
custom classes that override Equals, but they are certainly not true
value-types in terms of managed memory.

No, value types have to 'derive' from System.ValueType. This does not mean
that they inherit from it, however, as structures cannot inherit classes
(and System.ValueType is a class). Other value types, such as double, appear
as structures in Reflector and have 'ValueType' as a 'base type' without
seeming to make any other reference to ValueType in their members.
System.String makes no reference to ValueType in any of it's members either,
but derives directly from object; this class does share a number of
interfaces with value types (IComparable, IConvertible, IComparable<T>,
IEquatable<T>) which could lead to the 'value-type' semantics you refer to.

Chris.
 
D

Dustin Campbell

It also won't work with value types...
I'm not so sure. Lutz Roeder's refector declares Enum as a 'public
abstract class' and the System.Type class has a specific 'IsEnum'
property, the only listed value type to have its own specific 'IsX'
property. It may not a be a 'class' (IsClass returns false) as such,
but I'm not convinced that it's a struct either.

Structs are not really a CLR notion -- value types are. Part of the confusion
here is that the term "struct" and "value type" are being passed around as
synonomous -- which they really aren't. All C# structs are value types but
not all value types are C# structs.

At the CLR level, there are only classes. In fact, interfaces are actually
classes too but have a specific bit set that denotes them as interfaces and
causes them to be treated differently. A value type is simply a class that
derives from System.ValueType. An enum is a class that derives from System.Enum
(which derives from System.ValueType). The CLR simply treats classes that
derive from System.ValueType in a special way -- passing them by value instead
of by reference and allocating them on the stack when declared in a method
body.

Lutz Roeder's reflector can be a bit misleading if you leave the language
set to C#. Setting it to IL will show the reality of how types are really
declared. Consider this declaration of System.Int32 in IL:

..class public sequential ansi serializable sealed beforefieldinit Int32
extends System.ValueType
implements System.IComparable, System.IFormattable, System.IConvertible,
System.IComparable`1<int32>, System.IEquatable`1<int32>

It is really a class that derives from System.ValueType and implements a
set of specific interfaces. There is nothing remarkably different about how
System.Int32 is declared versus, say, System.StringComparer:

..class public abstract auto ansi serializable beforefieldinit StringComparer
extends object
implements System.Collections.IComparer, System.Collections.IEqualityComparer,
System.Collections.Generic.IComparer`1<string>, System.Collections.Generic.IEqualityComparer`1<string>

The only difference is the base type that it derives from. Enums are no different
-- consider System.StringComparison:

..class public auto ansi serializable sealed StringComparison
extends System.Enum

Enums *are* classes at the CLR level. Reflection attempts to make distinctions
between different types to clarify them for the client. So, it is not a great
tool to use to try and understand how things are really put together at the
metadata-level.
Which does not necessarily make it a structure.

You're correct, it makes it a value type. Clarification of terminology is
definitely needed here.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
D

Dave Sexton

Hi Chris,
I'm not so sure. Lutz Roeder's refector declares Enum as a 'public abstract
class' and the System.Type class has a specific 'IsEnum' property, the only
listed value type to have its own specific 'IsX' property. It may not a be a
'class' (IsClass returns false) as such, but I'm not convinced that it's a
struct either.

Interesting that Reflector shows "class" - probably because of Enum's abstract
nature, but it's definately a struct from which the CLR allows other structs
to derive.

I think this MSDN doc explains it best:

"Enum Structure"
http://msdn2.microsoft.com/en-us/library/system.enum.aspx

And here's some more interesting information on the subject:

"ValueType overrides the virtual methods from Object with more appropriate
implementations for value types. See also Enum, which inherits from ValueType.

Data types are separated into value types and reference types. Value types are
either stack-allocated or allocated inline in a structure. Reference types are
heap-allocated. Both reference and value types are derived from the ultimate
base class Object. In cases where it is necessary for a value type to behave
like an object, a wrapper that makes the value type look like a reference
object is allocated on the heap, and the value type's value is copied into it.
The wrapper is marked so the system knows that it contains a value type. This
process is known as boxing, and the reverse process is known as unboxing.
Boxing and unboxing allow any type to be treated as an object."

"ValueType Class"
http://msdn2.microsoft.com/en-us/library/system.valuetype.aspx

No, value types have to 'derive' from System.ValueType. This does not mean
that they inherit from it, however, as structures cannot inherit classes
(and System.ValueType is a class).

I wasn't referring to polymorphism at all. But now that you mention it, how
is derivation different from inheritance? I always thought they were the same
:)
Other value types, such as double, appear as structures in Reflector and
have 'ValueType' as a 'base type' without seeming to make any other
reference to ValueType in their members. System.String makes no reference to
ValueType in any of it's members either, but derives directly from object;
this class does share a number of interfaces with value types (IComparable,
IConvertible, IComparable<T>, IEquatable<T>) which could lead to the
'value-type' semantics you refer to.

You wrote, "Not all value types are structures". I was simply explaining that
classes with value-type semantics are not actual value-types when managed by
the CLR.

So it seems that all value-types are structures.

BTW, Reflector is a nice tool to have (I use it myself) but how it displays
classes or structs in its GUI might not be such a good indication of how they
are actually being managed by the CLR. I'd prefer to stick with the docs on
this one.
 
C

Christopher Ireland

Enums *are* classes at the CLR level. Reflection attempts to make
distinctions between different types to clarify them for the client. So,
it is not a great tool to use to try and understand how things are really
put together at the metadata-level.

Many thanks for your detailed reply, Dustin!

You're right, looking at the issue using the IL setting of Reflector is
somewhat more enlightening here than using the C# setting.

I don't suppose you know why the System.Type class has an IsEnum property
and not an IsDouble (or IsAnyOtherValueType) property? I am aware that it
has an IsValueType property. Why would enums need to be treated differently?

Thanks again,

Chris.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top