我想要具有可以传递、比较和识别的命名成员的不可变匿名类型——元组和匿名类型的合并。这不存在,我意识到这一点。
所以问题是:使用 C#4 或 5 有什么好的惯用替代品?
用例是来自异构数据源的流畅 LINQ 数据处理。总之,C# 中的 ETL。我做了一些非常复杂的分析,数据来自多个平台和来源。“将其全部放在同一个平台上并使用实体框架”不是一种选择。我希望能够流畅地传递本质上是任意记录的内容——不可变的命名只读属性集。
除了为每个其他匿名类型创建自定义不可变 POCO 之外,我唯一能想到的就是使用属性将编译的注释添加到返回Tuple
s 的方法中。当然,编写一个代码生成器来吐出不可变的 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# 参考)
Duke关键字允许在C# 的所有静态类型功能中使用匿名类型。与普通匿名类型一样,编译器会将具有相同数量、名称和属性类型的匿名类型视为具有相同类型。但是,duck 关键字还允许在成员声明中使用这些类型并作为泛型类型的类型参数。
1.鸭式实例
与匿名类型一样,duck 类型对象的实例只能使用没有类型名称的对象初始化器来创建。语法与普通匿名类型相同,只是 在new运算符之后添加了关键字duck:
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类型引用
可以使用鸭子类型字面量、鸭子类型别名来引用鸭子类型,或者在可以推断出属性或方法的返回类型时隐式地引用鸭子类型。
2.1 鸭子类型字面量鸭子类型可以用类型文字表示,它可以用来代替任何类型引用。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"]),
};
}