【问题标题】:Why does C# not allow generic properties?为什么 C# 不允许泛型属性?
【发布时间】:2012-01-27 02:21:51
【问题描述】:

我想知道为什么我不能像拥有泛型方法那样在非泛型类中拥有泛型属性。即:

public interface TestClass
{
   IEnumerable<T> GetAllBy<T>(); //this works

   IEnumerable<T> All<T> { get; } //this does not work
}

我读过@Jon Skeet 的answer,但这只是一个声明,很可能在规范中的某个地方。

我的问题是为什么实际上是这样的?这个限制是否避免了一些问题?

【问题讨论】:

  • 答案可能是“没有任何意义”。对我而言,属性与状态有关,而方法与行为有关。行为普遍适用而类型无关紧要是有意义的,实际上,同一方法普遍适用于同一实例中具有许多不同类型的许多不同执行,并且状态没有意义普遍适用于同一程度。如果你需要泛型状态,你需要一个泛型类。但我只是在沉思。
  • @AnthonyPegram 在这里。一个事物的属性参数多态有什么意义呢?事物的属性是它的颜色、高度、重量等。属性的全部是它们没有参数化。用形式参数对属性进行参数化是没有意义的,而且说“我希望我的 Car 类具有不同于 Weight 的 Weight 属性”当然也没有任何意义。用 type 参数化 property 是什么意思?
  • @EricLippert 通用属性看起来确实很方便,例如:class Figure { public ColorT Color&lt;ColorT&gt; { get { ... } } ... }(可能对ColorT 有一些限制)以在所需的颜色空间中返回颜色。或使用不同单位的数量,以参考您的示例。
  • 另外,GetWeight&lt;Giraffe&gt;(); 也没有意义,但我猜你不会认为泛型方法毫无意义。
  • @BartoszKP 这与那个电话是否有意义无关; Eric 评论中的关键是“属性的全部意义在于它们没有参数化”,&lt;Giraffe&gt; 肯定是一个参数——特别是泛型类型参数。

标签: c# generics properties language-design language-specifications


【解决方案1】:

从技术上讲,CLR 只支持泛型类型和方法,而不支持属性,所以问题是为什么它没有被添加到 CLR。对此的答案可能很简单,“它被认为没有带来足够的收益来值得付出代价”。

但更根本的是,它被认为没有带来任何好处,因为将属性通过类型参数化在语义上没有意义。 Car 类可能具有 Weight 属性,但拥有 Weight&lt;Fruit&gt;Weight&lt;Giraffe&gt; 属性是没有意义的。

【讨论】:

  • 我认为,如果您查看其他一些答案,他们不允许这样做是有原因的,这不仅仅是“时间过多”的问题。
  • @viggity:我的回答也不只是“太多时间”。另外,我在其他答案上留下了 cmets。简而言之,他们没有解决问题。
  • 你知道为什么 CLR 有三种属性(只读、只写和读写),而不是简单地指定一个编译器看到的东西看起来像对属性 foo 的访问应该检查是否存在名为 get__foo 或 set__foo 的兼容方法并具有 isProperty 属性?后一种实现看起来更容易,允许属性 getter 或 setter 作为委托传递而无需包装,并且有助于基类或接口具有抽象只读属性而派生类或接口具有读写属性的场景。
  • 我不明白“没有意义的语义论点”。与类上的通用方法一样,其目的通常是使类的子类型成为通用类,而不是将整个类变成它不是的东西(如将汽车变成水果的示例)。更有可能的是对通用类的内部组件进行一些操作(在属性的情况下获取或设置),例如内部与外部电子设备或燃料、制动器、转速表,其中操作同样适用于每个。听起来更有可能是在太硬的篮子里。
  • 这并不意味着泛型属性没有意义,因为Weight&lt;Fruit&gt; 没有意义。对于每个功能,您都可以构建一个愚蠢的示例,这并不意味着该功能根本没有意义。在这种情况下,请考虑Weight&lt;Kilogram&gt;,这确实有意义(或在我的问题下的评论中查看另一个示例)。
【解决方案2】:

Julian Bucknall 的这篇 Generic Properties 博客文章是一个很好的解释。本质上这是一个堆分配问题。

【讨论】:

  • 与其他答案一样,这篇博文根本没有解决泛型属性的问题,只涉及泛型字段。这并不能解释为什么我们不能拥有泛型属性。
  • 这正是我要说的。问题都在于fields。博客条目中没有任何内容说明为什么我们不能拥有没有字段的通用属性。属性只是一个 get 方法(以及可选的 set 方法),它完全可以是通用的。只有字段是不可能的。
  • 有很多get-only属性的例子,不需要字段支持,可以做基本的计算。因此,对于这些“场”理论不成立。实际上,我开始怀疑这个问题是因为这样一个属性。
  • 那个帖子太糟糕了。假设所有属性都有一些后备存储,但事实并非如此。
  • 想象一个异构集合类。拥有一个获取给定类型集合中所有对象的操作可能会很好。如果你有通用属性,那可能是collection.OfType&lt;Blah&gt;。相反,它必须是 collection.OfType&lt;Blah&gt;() 方法,即使它不带任何参数。
【解决方案3】:

我的猜测是它有一些令人讨厌的极端情况,使语法模棱两可。副手,这似乎很棘手:

foo.Bar<Baz>=3;

应该被解析为:

foo.Bar<Baz> = 3;

或者:

foo.Bar < Baz >= 3;

【讨论】:

  • 实际上,泛型方法已经存在这个问题(M(a&lt;b,c&gt;(d + 1))M(a &lt; b, c &gt; (d+1)),这比您的示例更不做作),因此特别决定无论如何都要进行重大更改。
  • @Timwi 这出乎意料。我会假设 a,b,c,d 是局部变量,而 M 是一个需要 2 个布尔值的方法。这将是最有意义的,因为如果您定义一个与字段、属性、非泛型方法,甚至是类型(泛型和非泛型)或命名空间,由于阴影,局部变量优先。很奇怪,阴影几乎适用于任何除了泛型方法。我并没有真正被这个限制所困扰,但我确实觉得它很奇怪。
【解决方案4】:

我认为不使用自动 getter/setter 说明了为什么没有在类级别定义“T”是不可能的。

尝试编码,自然会是这样:

IEnumerable<T> _all;
IEnumerable<T> All
{
    get { return _all; }
}

因为您的字段使用“T”,所以“T”需要在类上,CLR 知道“T”是什么。

当您使用方法时,您可以延迟“T”的定义,直到您实际调用该方法。但是对于字段/属性,“T”需要在一个地方声明,在类级别。

在类上声明 T 后,创建属性就变得非常容易。

public class TestClass<T>
{
    IEnumerable<T> All { get; }
}

用法:

var myTestClass = new TestClass<string>();
var stuff = myTestClass.All;

就像方法上的“T”类型参数一样,您可以等到实际实例化您的 TestClass 来定义“T”将是什么。

【讨论】:

  • 您的整个答案都是关于字段,而不是属性;您只是在使用 自动实现的属性 的概念来悄悄地引用隐式支持字段。这个答案没有解决泛型属性的问题,并且不是关于自动实现泛型属性的问题。
  • 一个更好的例子是 IEnumerable _all 和 IEnumerable All,它将迭代 _all T,产生任何 S 类型的 T。(请注意,这可以通过_all.OfType 扩展方法。)
【解决方案5】:

我做了类似的东西。 它在运行时键入检查。

public class DataPackage
{
    private dynamic _list;

    public List<T> GetList<T>()
    {
        return (List<T>)_list;
    }

    public void SetList<T>(List<T> list)
    {
        _list = list;
    }

    public string Name { get; set; }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-04
    • 2010-09-22
    • 2010-11-01
    • 1970-01-01
    • 2011-04-26
    • 2016-07-12
    相关资源
    最近更新 更多