Welcome to MSDN Blogs Sign in | Join | Help

Using Foo<T>(...) instead of Foo(System.Type, ...)

Whenever you're passing in a parameter of System.Type that is usually determined at compile-time, consider having a version with a generic parameter.  This converts runtime checks into compile time checks, may let you have more efficient code, and likely lets you get rid of ugly casts.

For example, a method like: static object Enum.Parse(Type enumType, string value), parses a string into a member of an enum. The example on MSDN wiki shows usage like:

    enum Colors { Red = 1, Green = 2, Blue = 4, Yellow = 8 };
    Colors myOrange = (Colors)Enum.Parse(typeof(Colors), "Red, Yellow");

In this case, your caller knew everything at compile time, yet you have a) an extra cast, and b) an extra parameter.  That's room for things to go wrong at runtime. It would be cleaner if you could just remove the cast and extra parameter and say:

    Colors myOrange = Enum.Parse<Colors>("Red, Yellow");

Compile-time vs. Runtime
The generic parameter is resolved at compile-time, (unless you're using something like late-bound invocation), which means you get errors sooner.
However, it also means that you have less flexibility for dynamic scenarios. For example, in the Enum.Parse case, the current signature lets you resolve everything at runtime. For example, you could read in the type-name and value all at runtime (perhaps from an XML config file) and parse on-the-fly. Eg:

        // Showoff runtime parameters being dynamic
        static int GetEnumValue(string typeName, string enumName) 
        {
            return (int) Enum.Parse(Type.GetType(typeName), enumName);  
        }

If you want that degree of flexibility, you could have both signatures. The generic version could be a wrapper over the non-generic version. Eg:

static object Enum.Parse(Type enumType, string value) // non-generic
static T Enum.Parse<T>(string value) {  // generic type-safe wrapper,
    return (T) Parse(typeof(T), value);
}

 

What about FxCop?
FxCop actually currently issues a warning against 'T Foo<T>()', but it's a little overzealous (the GenericMethodsShouldProvideTypeParameter rule). There's been great discussion of this on the fxcop forums (most recently on this thread), and the consensus seems that this pattern is ok.

Generics, COM, Guids:
Where this really gets cool is that it can also be great for COM-interop when you use the Type.Guid property. Recall that when you import a COM-classic interface into managed-code, you can use the GuidAttribute to associate the IID with the managed interface.  

For example, ICorDebugModule has a method to get a metadata interface from a module. It basically calls QueryInterface for the given RIID and returns that interface out via the ppObj:

        HRESULT GetMetaDataInterface([in] REFIID riid, [out] IUnknown **ppObj);

That can be imported into managed as:


        void GetMetaDataInterface(ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out IUnknown ppObj);

And you could write a pretty managed wrapper to use a return value instead of an out-parameter.


        public object GetMetaDataInterface (Guid interfaceGuid)
        {
            object obj;
            m_module.GetMetaDataInterface(ref interfaceGuid,out obj);
            return obj;
        }

And then use it like:

            Guid IID_IMetadataImport = new Guid("7DAC8207-D3AE-4c75-9B67-92801A497D44");
            m_importer = (IMetadataImport)managedModule.GetMetaDataInterface(IID_IMetadataImport);

But you could also put a generic wrapper on it and use the Type.Guid property:

         
        public T GetMetaDataInterface<T>()
        {
            object obj;
            Guid interfaceGuid = typeof(T).GUID;
            m_module.GetMetaDataInterface(ref interfaceGuid, out obj);
            return (T) obj;
        }

  

And then use it like:

        m_importer = managedModule.GetMetaDataInterface <IMetadataImport>();

No COM GUIDs, ugly runtime type-casts, runtime System.Type objects, or out parameters. 
 

Published Monday, October 09, 2006 7:51 PM by jmstall

Comments

# re: Using Foo&lt;T&gt;(...) instead of Foo(System.Type, ...)

Tuesday, October 10, 2006 3:21 AM by Udi Dahan - The Software Simplist
Generics are great, but you can't use them on ContextBoundObjects - not in 2.0, and apparently not in 3.0 either. Considering the difficulty in writing thread-safe rich client code without using a synchronization domain (which requires inheriting from ContextBoundObject), you might want to rethink this broad guidance.

# re: Using Foo<T>(...) instead of Foo(System.Type, ...)

Tuesday, October 10, 2006 6:40 AM by jeswin

# Types, Generics and Enums

Tuesday, October 10, 2006 10:31 PM by Javier G. Lozano

# test page

Wednesday, June 13, 2007 3:00 PM by Michael's Coding Den

.NET Tips &amp; Tricks Michael Nemtsev, Microsoft MVP Last update: June 13 , 2007 Document version 0

# .NET Tips & Tricks: Common

Wednesday, June 13, 2007 3:45 PM by Michael's Coding Den

Common INFO: · const is &quot;baked&quot; into the assembly. If you have to change the const value in

# .NET Tips & Tricks: Common

Wednesday, June 13, 2007 4:01 PM by Michael's Coding Den

This section describes the common .NET tips which don&#39;t relates to the specific category INFO: ·

# .NET Tips & Tricks: Common

Wednesday, June 13, 2007 4:06 PM by Michael's Coding Den

This section describes the common .NET tips which don&#39;t relates to the specific category INFO: ·

# .NET Tips & Tricks: Common

Wednesday, June 13, 2007 4:08 PM by Michael's Coding Den

This section describes the common .NET tips which don&#39;t relates to the specific category INFO: ·

New Comments to this post are disabled
 
Page view tracker