问题
I have the following method
public bool HasTypeAttribute<TAttribute, TType>(TType obj)
{
return typeof(TType).GetCustomAttribute<TAttribute>() != null;
}
and I want to be able to use it like this:
MyClass instance = new MyClass();
TypeHelper.HasTypeAttribute<SerializableAttribute>(instance);
but I can't get it working because of the
incorrect number of type parameters
so that I need to call
TypeHelper.HasTypeAttribute<SerializableAttribute, MyClass>(instance);
which certainly makes sense, but why can the compiler not infer the type of the passed object? Because if the method looked like this:
public void Demo<T>(T obj)
{
}
the compiler would certainly be able to infer the type, so that I can write
Foo.Demo(new Bar());
instead of
Foo.Demo<Bar>(new Bar());
So, is there a way to make type inference work in this case? Is it a design flaw by me or can I achieve what I want? Reordering the parameters doesn't help too...
回答1:
Because the C# rules don't allow that.
It would be feasible for C# to have a rule where if some of the types related to parameters (and hence could be inferred at least some of the time) and the number of explicitly given types was the same as the number of remaining un-inferrable types, then the two would work in tandem.
This would need someone to propose it, convince other people involved in the decision-making around C# it was a good idea, and then for it to be implemented. This has not happened.
Aside from the fact that features start off having to prove themselves worth the extra complexity they bring (add anything to a language and it is immediately more complex with more work, more chance of compiler bugs etc.) the question then is, is it a good idea?
On the plus, your code in this particular example would be better.
On the negative, everyone's code is now slightly more complicated, because there's more ways something that is wrong can be wrong, resulting either in code that fails at runtime rather than compile-time or less useful error messages.
That people already find some cases around inference to be confusing would point to the idea that adding another complicating case would not be helpful.
Which isn't to say that it is definitely a bad idea, just that there are pros and cons that make it a matter of opinion, rather than an obvious lack.
回答2:
You could break the call into multiple steps, which lets type inference kick in wherever it can.
public class TypeHelperFor<TType>
{
public bool HasTypeAttribute<TAttribute>() where TAttribute : Attribute
{
return typeof(TType).GetCustomAttribute<TAttribute>() != null;
}
}
public static class TypeHelper
{
public static TypeHelperFor<T> For<T>(this T obj)
{
return new TypeHelperFor<T>();
}
}
// The ideal, but unsupported
TypeHelper.HasTypeAttribute<SerializableAttribute>(instance);
// Chained
TypeHelper.For(instance).HasTypeAttribute<SerializableAttribute>();
// Straight-forward/non-chained
TypeHelper.HasTypeAttribute<SerializableAttribute, MyClass>(instance);
That should work alright for this case, but I'd warn against using it in cases where the final method returns void, because it's too easy to leave the chain half-formed if you're not doing anything with the return value.
e.g.
// If I forget to complete the chain here...
if (TypeHelper.For(instance)) // Compiler error
// But if I forget the last call on a chain focused on side-effects, like this one:
// DbHelper.For(table).Execute<MyDbOperationType>();
DbHelper.For(table); // Blissfully compiles but does nothing
// Whereas the non-chained version would protect me
DbHelper.Execute<MyTableType, MyDbOperationType>(table);
来源:https://stackoverflow.com/questions/48322044/infer-generic-type-with-two-generic-type-parameters