无法在C#中使用“内联”数组?


89

想象你在某处有这个

public static T AnyOne<T>(this T[] ra) where T:class
    {
    int k = ra.Length;
    int r = Random.Range(0,k);
    return ra[r];
    }

甚至就是这个

public static string OneOf(this string[] strings)
    {
    return "a";
    }

然后,您当然可以执行此操作...

string[] st = {"a","b","c"};
string letter = st.AnyOne();

...太好了。但。看来您不能这样做:

string letter = {"a","b","c"}.AnyOne();

或实际上也许

string letter = ( {"a","b","c"} ).AnyOne();

或其他我尝试过的方法。

实际上(1)为什么不能这样做?(2)我是否缺少某些东西,如果有办法,您将如何做?


5
我不确定重复的问题是否合适,OP并没有询问数组初始化程序,而是为什么编译器在分配对象之前才将其识别为数组。
罗恩·拜尔

4
我对C#术语不熟悉,但我相信这通常称为文字数组文字,而不是内联

3
该语法元素是数组初始化程序集合初始化程序,具体取决于使用它的上下文。在任何情况下都不将其分类为表达式
埃里克·利珀特

Answers:


129

您必须先使用创建数组new[]

string letter = (new[] {"a","b","c"}).AnyOne();

正如@hvd提到的,您可以在不使用括号的情况下执行此操作(..),我添加了括号,因为我认为它更易读。

string letter = new[] {"a","b","c"}.AnyOne();

您可以指定数据类型,new string[]就像在其他答案中提到的那样。


您不能仅仅这样做{"a","b","c"},因为您可以将其视为填充数组而不是创建数组的一种方式。

另一个原因是编译器会很困惑,不知道要创建什么,例如a string[]{ .. }或a List<string>{ .. }

仅使用new[]编译器就可以通过数据类型"..")知道所需的{..}string)。必不可少的部分是[],这意味着您需要一个数组。

您甚至无法使用创建空数组new[]

string[] array = new []{ }; // Error: No best type found for implicity-typed array

13
您不需要那些括号。string letter = new[] {"a","b","c"}.AnyOne();很好 如果您想要它们,如果您认为它在括号中更易读,那么它们是有效的,但在那种情况下,我认为至少值得一提的是,这是您的有意识选择,并非由语言决定的

我知道语法new [] {1,2},但是有没有更简单的语法?像[1,2]吗?
seguso

51

(1)为什么不能这样做? {"a","b","c"}.AnyOne();

这行:

string[] st = {"a","b","c"};

是短手的等效数组创建表达式 (下ILSpy

string[] st = new string[]  {"a","b","c"};

string[] st = {"a","b","c"} 只能在声明时使用,您不能在其他地方使用,甚至不能:

string[] st;
st = {"a", "b", "c"}; //Error

在第7.6.10.4节中以C#语言规范中的数组创建表达式对此进行了说明。

因此,"{"a", "b", "c"}"仅在声明中不使用它就没有任何意义。因此,您不能将其与扩展方法一起使用,因为扩展方法在数组上运行。

(2)我是否缺少某些东西,如果有办法,您将如何做?

@adricadar的答案中已经提到,您可以执行以下操作:

(new[] {"a","b","c"}).AnyOne();

要么

(new string[] {"a","b","c"}).AnyOne();

48

我回过头来回答“为什么不”的问题,因为首先,答案几乎永远不会令人满意-您已经得到了答案:“功能不是您想要的方式,因为规范没有说您想要说的话” ,我想这不是一个特别令人满意的答案。其次,设计团队不必证明为什么世界不是您想要的样子。功能不是免费存在的,而是根据语言设计的;相反,必须先对功能进行论证,然后再进行设计。

因此,让我们尝试使您的“为什么不”问题更加清晰。现有功能是“可以在(a)初始化中的等号的右侧或(b)数组类型的对象构造的右侧使用数组初始化器”。建议的功能是:“数组初始化器也可以用作表达式”。问题是“埃里克会对提议的功能提出什么批评?”

我要提出的第一个批评是,不清楚表达的类型是什么。在变量初始化器中,您具有变量的类型,在对象创建表达式中,您具有对象的类型;从这两个我们可以推断出构造数组的类型。没有任何提示,我们应该推断出哪种类型?

在C#1.0中,添加了此功能后,使用该语言进行了零类型推断。C#早期的设计原则是“毫无意外”,并且编译器不是“太聪明”。如果开发人员希望表达式具有特定类型,则该类型在表达式中应以某种方式显而易见。当你说

new double[] { 1, 2, 3.4 }

很明显是什么类型的。相似地

new Animal[] { cat, dog, null }

建议的功能违反了该原则。表达式必须具有类型,但绝不能清除参数的类型

M({cat, dog, null})

此外:假设我们有两个重载M,其中一个重载采用的数组,Animal而一个重载则采用的数组IPet。哪个过载M?其中一项转换比另一项转换好吗?元素的类型为CatDog; 推论甚至没有出现在那里的类型是否有意义?这些都是设计团队必须考虑的问题,而这些问题绝不是显而易见的答案。拟议中的功能可以在很短的时间内将我们带入深水区。

现在,C#3.0解决了这个问题,因为C#3.0添加了许多功能,在这些功能中,编译器代表开发人员推断类型。早期有关“没有意外”和“简单规则”的原则与使LINQ工作所需的其他设计原则相冲突。您应该在C#3.0中添加您建议的功能吗?

可能是这样。实际上在C#3.0中添加的功能是:

new[] { x, y, z }

使用算法推断数组的类型:获取具有类型的元素的表达式,确定这些类型中的哪一个是所有其他表达式可转换为的唯一的最通用类型,如果存在这种类型,则选择该类型。否则会产生错误,

可以进一步放松该功能以使其成为new[]可选功能。尚未完成。

现在,如果您在C#3.0的时间范围内要求我批评所建议的功能,我会指出(1)C#3.0编译器已经很可能会拖延整个发行版的时间表,因此,我们不再添加任何其他内容设计,实现和测试的负担,这是一项完全不必要的功能,可为用户节省六次按键操作;(2)C#3.0还添加了集合初始化程序:

new List<int>() { 10, 20, 30 }

为什么要{10, 20, 30}自动成为数组?为什么不应该是List<int>?还是其他多种类型中的任何一种?为什么偏向数组?请记住,一旦我们选择为数组提供语法,我们将永远受其困扰。可能再也没有其他东西了,因此建议的功能不仅不必要,而且还可以防止将来似乎合理的功能。

总结:提议的功能直接违反了C#1.0的某些设计原则。它给C#3.0增添了不必要的负担。自C#3.0以来,在该语言的所有版本中,与许多其他更有价值的功能相比,建议的功能都没有很好的理由建议花时间,精力和金钱在它上面。

因此,没有这样的功能。


嘿,您需要在上面印有“世界不是您想要的世界”的 T恤:)
slugster 2015年

6
@JoeBlow:首先,非常欢迎。关于“为什么不”-您的评论很好地说明了问题。当有人问“为什么”的问题时,他们正在寻找合乎逻辑的理由。有些人正在寻找务实的理由。而且您显然正在寻找描述规则的规范行。如此含糊,以至于很难在提问者的脑海中拟出一个针对该问题的好的答案。“为什么不”的问题甚至更糟,因为它们对于甚至不存在的事物都是模糊的问题
埃里克·利珀特

3
@EricLippert甚至比关于_might_存在问题的问题还要糟糕:如果您正在与团队一起开发软件,那么整个团队一年中的每一天都会花时间在思考功能并平衡后果。做出决定。功能要求和“为什么”要求理由。但是,为什么不意味着有人只是想做某件事,这从根本上质疑了团队本身的判断。更糟糕的是,问为什么不提问的人通常对该主题知之甚少。因此,我认为推迟为什么不提出质疑是完全公平的。海事组织好。
atlaste 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.