Using a generic type argument in place of an argument of type System.Type. Is it a smell? [closed]

断了今生、忘了曾经 提交于 2019-12-10 12:32:41

问题


I often see (in many mocking libraries for example) methods where a generic type argument is used in place of an argument of type System.Type. I am specifically talking about cases where generic type is only being used in typeof(T) operation (i.e. no instance of type T is being used anywhere within the method, and T is not being used for either return type or other arguments).

For example consider following method:

public string GetTypeName(System.Type type) { return type.FullName; }

this method is often accompanied with a generic version:

public string GetTypeName<T>() { return GetTypeName(typeof(T)); }

Questions is it a bad practice or a good practice?
Is this a syntactic sugar or are there more to it?

I see this as misusing a language feature to abbreviate a call to a method that accepts an argument of type System.Type

Would you consider this a smell? Should this be avoided? or is this actually a good practice (to provide a generic method as a shortcut to avoid typing typeof()).

Here are some practical issues with using this pattern I can think of:

  1. if an argument of non System.Type type is added - method might need to be rewritten (if order of arguments is semantically significant) to non generic version (otherwise some arguments will be generic type arguments, and some will be regular arguments).
  2. it requires two methods (generic and non generic for cases where type is not known at compile time). Consequently adds unit tests which are mostly meaningless.

On the other hand this is a common practice (and majority is always right, right?) but more importantly ReSharper prefers that signature when I do Extract Method refactoring on a code that requires single argument of type System.Type known at compile time (and I learned to take their recommendations though not on faith, but seriously).


回答1:


I think you need to consider documentation. How obvious is it what the methods do? If you have two methods (one with Type and one with a type argument), users need to look at both and choose. People who aren't looking at your code may not realize that the second one simply calls the first.

Where it definitely makes sense to use both is when the type argument is actually used when it can be and there is some kind of fallback for the Type version. For example:

object GetThingOfType(Type type) { ... }

T GetThingOfType<T>() { return (T)GetThingOfType(typeof(T)); }

Another thing to consider: A type argument must always be written explicitly. If it is likely that there will be more than one operation to perform with the same type object, it's not helpful to use type arguments. Consider something like this:

var t = typeof(string);
var name = GetTypeName(t);
var assemblyName = t.Assembly.FullName;

Even though I know the type is string, I should not write GetTypeName<string> here because I would be repeating myself. By giving me an option that I would most often be better off not choosing, you're adding a bit of unnecessary complexity.

A more obscure point is IDE support of XML documentation. You document the type argument like this:

<typeparam name="T">important information</typeparam>

Then if you type GetTypeName< in C#, Visual Studio will show "T: important information". But, for some reason, when you type GetTypeName(Of in Visual Basic, it will not (as of 2012).




回答2:


You are right: consider the semantics of the method. Is it operating on instances of the type, or on the type itself?

If it is operating on instances, then it should be a generic method. If it is on the type, then make it an argument of type Type.

So in your example I would say

public string GetTypeName(System.Type type) { return type.FullName; }

Whereas

public static int Count<TSource>(this IEnumerable<TSource> source)

Is operating on the instance source of type IEnumerable<TSource>.

In general, I have seen generics abused more than well-used. Any generic method implementation that does a typeof(T) or worse still, uses any kind of reflection is not really generic in my opinion and is an abuse. After all, generic means it works the same regardless of the type argument, doesn't it?

So, in summary, I agree with you - it smells.




回答3:


I don't use the string GetName<T>() { return typeof(T).Name; } pattern as it is a misuse (misuse is probably strong but I can't think of the right word) of the design pattern that is the reason for generics, namely: generic type parameters are there for the compiler and the JITter (see the answer to this question) so that they can generate the type specific storage, parameters, stack variables, etc etc.

Using it as a convenient method for passing a type argument at runtime to me smells. There are times when typeof(T) is necessary, but I've found them to be rare and usually needed only when doing complex things with generics, as opposed to simple type safety sorts of things. If I see it, I definately pause and ask myself why it's there.




回答4:


As it was already said, it looks to me like a matter of personal preferences. From my experience, most of the methods accepting an argument of type Type can be syntactically "sweetened" using an accompanying extension method.

So, in case of your example, I would make the second method an extension. Using this approach, you get a solution for your second concern - no unnecessary unit tests are required. The first one, however, remains; but then, addition of arguments will require refactoring anyway, so it will provide an opportunity to change the consumers of the extension method so they use the modified version of the original one.

Of course, it's just a personal opinion.




回答5:


A generic method has an advantage over a method with a parameter of type Type, because it can reffer to other generic things using the provided type. This is particularly useful in the following scenario:

public string GetTypeName<T>()
{ 
    return Cache<T>.TypeName;
}

private static class Cache<T>
{
    public static readonly TypeName = GetTypeName(typeof(T));
}

This cache is easy, there is no need to mess around with dictionaries and it is automatically thread-safe.

This being said, if the implementation is not using this possibility, than the difference between the two is just cosmetic.




回答6:


Methods that deal with types usually do just that: Dealing with types.

IMO, Class.Method<SomeType>(); is much better than Class.Method(typeof(SomeType));

But that is a matter of opinion I guess.

Consider LINQ's .OfType<T>(), for example:

personlist.OfType<Employee>().Where(x => x.EmployeeStatus == "Active");

versus:

personlist.OfType(typeof(Employee)).Where(x => ((Employee)x).EmployeeStatus == "Active");

which one would you prefer?



来源:https://stackoverflow.com/questions/18257044/using-a-generic-type-argument-in-place-of-an-argument-of-type-system-type-is-it

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!