Partager via


Using Foo(...) 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.

Comments

  • Anonymous
    October 09, 2006
    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.

  • Anonymous
    October 09, 2006
    I faced a similar choice in an earlier project, blogged at <a href="http://www.process64.com/MikeStallOnFooTVsFooTypeType.aspx">http://www.process64.com/MikeStallOnFooTVsFooTypeType.aspx</a>

  • Anonymous
    June 13, 2007
    The comment has been removed

  • Anonymous
    June 13, 2007
    Common INFO: · const is &quot;baked&quot; into the assembly. If you have to change the const value in

  • Anonymous
    June 13, 2007
    This section describes the common .NET tips which don&#39;t relates to the specific category INFO: ·

  • Anonymous
    June 13, 2007
    This section describes the common .NET tips which don&#39;t relates to the specific category INFO: ·

  • Anonymous
    June 13, 2007
    This section describes the common .NET tips which don&#39;t relates to the specific category INFO: ·