【发布时间】:2012-10-22 16:18:05
【问题描述】:
我想要具有可以传递、比较和识别的命名成员的不可变匿名类型——元组和匿名类型的合并。 This doesn't exist,我意识到了这一点。
所以问题是:使用 C#4 或 5 有什么好的惯用替代方法?
用例是来自异构数据源的流畅 LINQ 数据处理。总之,C# 中的 ETL。我做了一些非常复杂的分析,数据来自多个平台和来源。 “将其全部放在同一个平台上并使用实体框架”不是一种选择。我希望能够流畅地传递本质上是任意的记录——不可变的命名只读属性集。
除了为每个单独的匿名类型创建自定义不可变 POCO 之外,我唯一能想到的方法是使用属性将编译的注释添加到返回 Tuples 的方法中。当然,编写一个代码生成器来吐出不可变的 POCO 并不难,但我讨厌这会使项目的对象模型变得混乱。使用dynamic 完全消除了静态类型的所有性能和设计时有用性,尤其是在从其他方法的结果中组合进一步查询时,所以我不认为它是一个可行的解决方案。
// My idea: Adding a attribute to methods to at least record the names
// of the "columns" of a Tuple at a method level
public class NamedTupleAttribute : Attribute {
public string[] Names { get; private set; }
public NamedTupleAttribute(string[] Names) { this.Names = Names; }
}
// Using NamedTuple attribute to note meaning of members of Tuple
[NamedTuple(new[] { "StoreNumber", "Gross", "Cost", "Tax" })]
public IEnumerable<Tuple<int, decimal, decimal, decimal>> GetSales { ... }
我想要什么(C# 6 的虚构 MSDN 文档):
鸭(C# 参考)
duck 关键字允许在 C# 的所有静态类型功能中使用匿名类型。与普通匿名类型一样,编译器会将具有相同数量、名称和属性类型的匿名类型视为具有相同类型。但是,duck 关键字还允许在成员声明中使用这些类型并作为泛型类型的类型参数。
1。鸭子类型实例
像匿名类型一样,duck 类型对象的实例只能使用对象初始化器创建 没有类型名称。语法与普通匿名类型相同,除了关键字 duck 在 new 运算符之后添加:
var record = new duck { StoreNumber=1204,
Transaction=410,
Date=new DateTime(2012, 12, 13),
Gross=135.12m,
Cost=97.80m,
Tax=12.11m };
2。鸭子类型参考
可以使用duck 类型文字、duck 类型别名或隐式引用Duck 类型,当可以推断出属性或方法的返回类型时。
2.1 鸭子类型文字duck 类型可以用类型文字来表示,它可以用来代替任何类型引用。 Duck 类型文字由关键字 duck 后跟名称 - 类型标识符对列表组成,就像在方法的参数列表中一样,除了用花括号括起来:
// A duck type literal:
duck { int StoreNumber, int Transaction, DateTime Date, decimal Gross, decimal Cost, decimal Tax }
// In a member declaration:
public duck { int StoreNumber, int Transaction, DateTime Date,
decimal Gross, decimal Cost, decimal Tax } GetTransaction(...) { ... }
// As a type parameter:
var transactions = new List<duck { int StoreNumber, int Transaction, DateTime Date,
decimal Gross, decimal Cost, decimal Tax }>();
2.2 鸭子类型别名
您可以在 C# 代码文件或命名空间中的命名空间 using 指令之后立即使用 using 指令为鸭子类型分配别名。然后可以使用别名代替任何类型引用。
// Namespace directives:
using System;
using Odbc = System.Data.Odbc;
// duck type aliases:
using TransTotal = duck { int StoreNumber, int Transaction, DateTime Date,
decimal Gross, decimal Cost, decimal Tax };
// Member declaration:
public TransTotal GetTransaction(...) { ... }
// As a type parameter:
var transactions = new List<TransTotal>();
2.3 推断的鸭子类型
如果可以推断出属性或方法的返回类型,则可以在成员声明中省略鸭子类型文字的主体:
// Long form:
public duck { int StoreNumber, int Transaction, DateTime Date,
decimal Gross, decimal Cost, decimal Tax } GetDummyTransaction() {
return new duck { ... };
}
// Short form:
public duck GetDummyTransaction() {
return new duck { ... };
}
// Short form as a type parameter:
public IEnumerabe<duck> GetTransactions(...) {
return
from record in someProvider.GetDetails(...)
where ((DateTime)record["Date"]).Date == someDate
group record by new {
StoreNumber = (int)record["Number"],
Transaction = (int)record["TransNum"],
Date = (DateTime)record["Date"]
} into transTotal
select new duck {
transTotal.Key.StoreNumber,
transTotal.Key.Transaction,
transTotal.Key.Date,
Gross = transTotal.Sum(x => (decimal)x["Gross"]),
Cost = transTotal.Sum(x => (decimal)x["Cost"]),
Tax = transTotal.Sum(x => (decimal)x["Tax"]),
};
}
【问题讨论】:
-
很遗憾那里没有动态鸭子。
-
就我个人而言,我很乐意为每个其他匿名类型编写一个自定义的不可变 POCO。我自己编写了许多复杂的 ETL 代码,相信我,使用完整的 XML cmets 将所有内容显式写出来对大脑来说更安全、更容易。
-
这里有问题吗?这似乎应该在视觉工作室用户语音网站上,而不是在这里:visualstudio.uservoice.com
-
@ReedCopsey 是的,问题在第二行:“什么是惯用的解决方法?”但我绝对可以将提议的
duck关键字移至 uservoice。我不熟悉它,所以谢谢你的链接。 -
可能很傻,但我认为扭曲一些模拟框架(如 Moq)来创建一些不可变对象(通过方法或属性获取器)是合理的。这可能在本地方法范围内工作(因为匿名类型仍然是“真实”类型),但要传递它,您可能仍然必须声明不可变接口某处。到那时,您不妨自己声明不可变的 POCO。
标签: c# linq tuples anonymous-types