【问题标题】:Infer generic type with two generic type parameters [duplicate]使用两个泛型类型参数推断泛型类型
【发布时间】:2018-01-18 13:02:42
【问题描述】:

我有以下方法

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());

那么,在这种情况下,有没有办法让类型推断起作用?这是我的设计缺陷还是我可以实现我想要的?重新排序参数也无济于事......

【问题讨论】:

  • 你可以传入一个object 并使用obj.GetType 而不是typeof(TType)
  • @juharr 不会给出相同的结果。
  • @JonHanna 为什么不呢?如果对象与泛型类型相同,它应该可以工作。
  • @ThomasFlinkow 我的意思是你会有public bool HasTypeAttribute&lt;TAttribute&gt;(object obj)。基本上它不能做部分泛型类型推断。它必须推断所有这些或必须指定所有。
  • @juharr,如果对象与泛型类型不同,它的工作方式会有所不同。只有当对象的实际具体类型 (object.GetType()) 与编译器从中推断的表达式的类型匹配时,它们才会相同,如果该类型是基类型或接口,则情况并非如此。

标签: c# generics type-inference


【解决方案1】:

因为 C# 规则不允许这样做。

如果某些类型与参数相关(因此至少在某些时候可以推断出来)并且显式给定类型的数量与剩余类型的数量相同,那么 C# 有一个规则是可行的不可推断的类型,那么两者将协同工作。

这需要有人提出建议,说服参与 C# 决策的其他人这是一个好主意,然后才能实施。这没有发生。

除了功能开始必须证明自己值得它们带来的额外复杂性这一事实(将任何东西添加到语言中,它会立即变得更加复杂,需要更多的工作,更多的编译器错误的机会等),那么问题是,这是个好主意吗?

另外,您在这个特定示例中的代码会更好。

不利的一面是,现在每个人的代码都稍微复杂了一点,因为出错的方式有更多可能出错,导致代码在运行时失败而不是编译时失败,或者有用的错误消息更少。

人们已经发现一些关于推理的案例令人困惑,这表明添加另一个复杂案例没有帮助。

这并不是说这绝对是一个坏主意,只是说它的优点和缺点使它成为一个见仁见智的问题,而不是明显的缺乏。

【讨论】:

    【解决方案2】:

    可以将调用分解为多个步骤,从而让类型推断尽可能发挥作用。

    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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多