我有以下方法
public bool HasTypeAttribute<TAttribute, TType>(TType obj)
{
return typeof(TType).GetCustomAttribute<TAttribute>() != null;
}
我希望能够像这样使用它:
MyClass instance = new MyClass();
TypeHelper.HasTypeAttribute<SerializableAttribute>(instance);
但我无法让它工作,因为
类型参数数量不正确
所以我需要打电话
TypeHelper.HasTypeAttribute<SerializableAttribute, MyClass>(instance);
这当然是有道理的,但是为什么编译器不能推断传递对象的类型呢?因为如果方法看起来像这样:
public void Demo<T>(T obj)
{
}
编译器肯定能够推断出类型,这样我就可以写
Foo.Demo(new Bar());
而不是
Foo.Demo<Bar>(new Bar());
那么,在这种情况下,有没有办法使类型推断起作用呢?是我的设计缺陷还是我能实现我想要的?重新排序参数也无济于事...
您可以将调用分解为多个步骤,这样就可以在任何可能的地方启动类型推理。
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);
在这种情况下,这应该可以正常工作,但我警告不要在最终方法返回 void 的情况下使用它,因为如果您不对返回值执行任何操作,则很容易使链保持半成形。
例如
// 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);
因为 C# 规则不允许这样做。
C# 有一个规则是可行的,如果某些类型与参数相关(因此至少在某些时候可以推断)并且显式给定类型的数量与剩余的不可推断类型的数量相同,那么两者将协同工作。
这需要有人提出它,说服参与 C# 决策的其他人这是一个好主意,然后实现它。这并没有发生。
除了功能开始必须证明自己值得它们带来的额外复杂性(向语言添加任何内容,并且随着更多的工作,编译器错误的机会等,它立即变得更加复杂)之外,问题是,这是一个好主意吗?
另外,您在此特定示例中的代码会更好。
消极的方面,每个人的代码现在都稍微复杂一些,因为错误的东西可能出错的方式更多,导致代码在运行时失败,而不是编译时失败,或者不太有用的错误消息。
人们已经发现一些关于推理的案例令人困惑,这表明添加另一个复杂的案例是没有帮助的。
这并不是说这绝对是一个坏主意,只是有利有弊,使其成为一个意见问题,而不是明显的缺乏。