Share via


typedef is good for your wrist but not for your social life

Sometimes, we find many of our favourite features missing in a language. The instant reaction is that we crib (yea, I do this a lot :-) ) and raise it as a short-coming of that language. Most language features are pretty simple to be implemented in a compiler and we miss the point that it could be that the language-designer deliberatly omitted that because it either does not fit in the language's general design or he considers it to be a bad programming practice.

A classic example of this is typedef. C++ users are very familiar with it and have abused this to no end. While some uses of it could be ok, people typically go overbeoard using this. Sometime back I used the following

 typedef BOOL (WINAPI *lpfn) (HWND hWnd, COLORREF cr, 
              BYTE bAlpha, DWORD dwFlags);
lpfn g_pSetLayeredWindowAttributes = 
           (lpfn)GetProcAddress(hUser32,
               "SetLayeredWindowAttributes");

I think this is a legitimate use where I used typedef to create a alias for a function pointer to a Win32 api. 

The C++ world is different, there you play around with global functions, function pointers, cross-platform issues. In C++ applications its typical to see typedef or #define being used to create types like NUMBER, TCHAR to switch types based on compilation options and also for cross-platform support where you want NUMBER to be 16-bit unsigned int irrespective of the plaform. But these things make code very un-maintainable. I have spent countless hours traversing definition chains and it irritates a poor guy trying to debug a piece of code late in the evening to find out customer_list ultimately boils down to char * after 3-4 indirections. 

I am not against typedefs, I have used it and have benefitted from other frameworks using it and its good for your wrist (less typing == less pain). But in C# it simply doesn't fit in and so C# doesn't have typedef. This is something to happy about and not a short-coming of the language. A lot of pain-points of C++ are not present in C# and hence you do not need typedefs that much.

using != typedef

Actually its wrong to say C# doesn't have typedef. It does have it in the form of using. But this is limited in terms of usage and functionality. You can do the following...

 usingEmpList = System.Collections.Generic.List<MyNameSpace.Employee >; usingNUMBER = System.Int32 ; 
namespace MyNameSpace{    public class Employee    {    }
    public class Class1    {        public Class1()        {            EmpList empList = newEmpList();             NUMBER num = 5;         }    }
}

So you can create alias for int and EmpList for a generic list of employees. The reason why this does not get as bad as typdefs is because of the following reasons

  1. You can have using clause only at the top of a namespace (using must precede all other namespace elements othern than extern alias declaration).
  2. using supports aliasing only classes and namespaces and not arbitrary types
  3. And most importantly aliases defined using using is visible only in that file(compilation unit). Actually the exact same thing is true for C++ as well. However in C++ you can put it in a .h file and include it in as many .cpp file you want. C# does not have the concept of including source files and so everything is just fine.

Not having typedef is not bad

actually its very good.

Just because a language does not have a feature doesn't mean that it has to be bashed. In one thread I saw someone bashing C# becuase he couldn't do using NUMBER=int; globally. What I couldn't figure out is why do you want to do this? In C# int is anyway an alias for System.Int32 and for any system on which .NET is ported (Mono/Rotor project) int will be ported for you to support 32-bit signed integer and you should not be bothered with doing it yourself. If you think NUMBER gives more clarity, think twice. Wear a maintainers hat and think if you come to that code 2 years later will NUMBER look more clear or int. Even List<Employee> is much more readable than EmpList (see below).

Some people suggest working around this limitation (???) using inheritance as follows.

 public class EmpList :     System.Collections.Generic.List<typedef.Employee>{}

I think this simply doesn't scale. For example you'll need to create EmpList constructors that match those od List<T>.

Comments

  • Anonymous
    February 01, 2006
    And what to do with constructs like the following?

    template< class T >
    class A{
    public:
    typedef T internal_type;
    };

    Maybe something like:

    class A< T >{
    public Type InternalType{
    get{ return typeof( T ); }
    }
    }

    Or have I missed something important?
  • Anonymous
    February 01, 2006
    The comment has been removed
  • Anonymous
    February 01, 2006
    Mea culpa.

    I missed a part of code that shows the usability of typedef in C++.

    I ment that using C++-like declaration it is possible to create an instance of 'unknown type' (in this case A::internal_type) using its constructor.

    e.g.:
    typename A::internal_type obj = A::internal_type();

    (I think it is general usage of typedefs in STL)

    But in C# it is possible to call the constructor only using Activator.CreateInstance (AFAIK). Isn't it more expensive operation?

    Now, after some time spent thinking I see that it is possible to implement C++-like functionality other way, but... as for me it's still not so elegant.

    I still in doubt whether it is possible to get the generic parameter type as type(kind of a symblolic name) but not as Type object?
  • Anonymous
    February 02, 2006
    The comment has been removed
  • Anonymous
    February 02, 2006
    Well. I do understand that (about constraints).

    Let's write the whole example of what I'm trying to say. :)

    // somewhere in the code using STL
    // important: outside the template container declaration
    template< class Container >
    void my_foo( const Container &c ){
    typename Container::const_iterator ci = Container.begin();
    // or maybe
    typename Container::value_type o = Container::value_type();
    }

    Thus one can create an instance of a really unknown type.

    In the following code we can implement almost same functionality.

    tamplate< class T >
    void my_foo( const std::list< T > &c ){
    // in this case
    typename std::list< T >::value_type o1;
    // is equal to
    T o2;
    }

    But! Here we are forced to use only std::list as a container.

    Maybe it's not a real life example (though I've used it not once). Maybe it is posssible to avoid such a problem in C# using some methods (ICollection<T> for example). But I still find such a code in C++ very cool. ;)

    PS: Thank you for attention paid.
  • Anonymous
    February 03, 2006
    The comment has been removed
  • Anonymous
    February 21, 2006
    I seem to recall something about generics not being CLS-compliant.  Therefore, to expose them in a CLS-compliant manner, one creates EmployeeList as a subclass of List<Employee>.
  • Anonymous
    February 23, 2006
    The person who can say "C++ templates are actually glorified macros" doesn't know a thing about templates.  And he's not likely to be aware of the role typedef plays in templates and generic metaprogramming.

    All I see here can be summarized in one sentence: "C# doesn't have a <feature>, so we don't need this <feature> and it's actually dangerous."  Kind of like the point of view that Java adepts used to have (still having?) with regard to Java vs other languages (J++, C#, C++).
  • Anonymous
    February 23, 2006
    The comment has been removed
  • Anonymous
    March 22, 2006
    The comment has been removed
  • Anonymous
    July 10, 2006
    The comment has been removed
  • Anonymous
    September 04, 2006
    I think you're missing one of the uses of a global using/typedef style function and that is to ensure all variables/parameters are of the right type.

    Let's say you have a Customer class and you decided to make the ID an Int16.  

    You will have parameters that occasionally need to take ID's to grab stuff from the databases and probably collection classes like Dictionary<int, Customer> to be able to access by Customer ID.

    You run out of Customer ID's and need to change the type.

    What you have to do now is druge through the code trying to work out which "Int32"'s need to change when in fact it would have been great to say in your customer class

    using CustomerIDType as Int32;

    and then in all your classes and parameters just put "CustomerIDType".

    Yes, this isn't as clear to new users as to what type that will be but it's part of the whole encapsulation concept of OO.

    The type of the CustomerID is an internal detail that should be hidden because you shouldn't care.  Unfortunately current OO semantics often mean that such internal implementation details "leak-out".

    The other even better option would be to have a compile-time version of typeof (that obviously was able to grab the type of instance variables).

    Dictionary<typeof(Customer.ID), Customer> = ...

    [)amien
  • Anonymous
    March 07, 2007
    The typedef is very useful in connection with template parameters; the standard template library uses them a lot.Calling templates "glorified macros" is just like calling inline functions "glorified macros", it's plain non-sense.I also fail to see what is wrong with public class EmployeeList being derived from List<Employee>. In fact it could be a great language feature if typedef List<Employee> EmployeeList; would create such a light weight inheritance (including auto-generated constructors) in C#. What is easier to understand, SortedDictionary<String,Int64> or FileLengthDictionary ?
  • Anonymous
    September 18, 2007
    The comment has been removed
  • Anonymous
    October 07, 2007
    The comment has been removed
  • Anonymous
    November 26, 2007
    The comment has been removed
  • Anonymous
    August 02, 2008
    Absence of typedefs is a huge blunder in C# language, and should be corrected ASAP. Things like personID and accountID shouldn't be integers, compiler must ensure that programmers don't pass accountID where personID is expected. The only way to accomplish this kind of type safety in c# is to write class wrappers around these IDs, which is huge and unnecessary code bloat and run time processing overhead.