我希望不可变的匿名类型具有可以传递,比较和识别的命名成员 - 元组和匿名类型的合并。 This doesn't exist,我意识到了这一点。
所以问题是:使用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文档):
duck 关键字允许在C#的所有静态类型功能中使用匿名类型。与普通的匿名类型一样,编译器会将具有相同数量,名称和属性类型的匿名类型视为具有相同类型。但是,duck关键字还允许在成员声明中使用这些类型,并将其用作泛型类型的类型参数。
与匿名类型一样,只能使用对象初始值设定项创建duck类型对象的实例 没有类型名称。除了关键字之外,语法与普通匿名类型的语法相同 在新运算符后添加 duck :
var record = new duck { StoreNumber=1204,
Transaction=410,
Date=new DateTime(2012, 12, 13),
Gross=135.12m,
Cost=97.80m,
Tax=12.11m };
Duck类型可以使用duck类型文字,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指令为鸭类型分配别名。然后可以使用别名代替任何类型的引用。
// 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推断的鸭子类型
如果可以推断属性或方法的返回类型,则可以在成员声明中省略duck类型文字的主体:
// 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"]),
};
}
答案 0 :(得分:6)
ExpandoObject感兴趣。
答案 1 :(得分:1)
好像你想要实现自己的IDynamicObjectProvider: http://msdn.microsoft.com/en-us/library/system.dynamic.idynamicmetaobjectprovider.aspx
示例实施: http://msdn.microsoft.com/en-us/vstudio/ff800651.aspx
您似乎想要访问像List&gt;这样的结构其中String是名称,Type是值类型,Object是值。
但这似乎是一个很大的麻烦,可能不会提供非常好的表现。您应该只需要实现所需的所有类。为了理解那些必须在你之后维护代码的人,为每个输入定义接口似乎是合理的。
答案 2 :(得分:1)
您可能想看看这种方法:
public IEnumerable<T> GetTransactions<T>(...,
Func<int, int, DateTime, decimal, decimal, decimal, T> resultor) {
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 resultor(
transTotal.Key.StoreNumber,
transTotal.Key.Transaction,
transTotal.Key.Date,
transTotal.Sum(x => (decimal)x["Gross"]),
transTotal.Sum(x => (decimal)x["Cost"]),
transTotal.Sum(x => (decimal)x["Tax"])
);
}
resultor
Func
映射到具体duck
的布局,使用匹配类型获取尽可能多的参数,并返回T
。当您通过方法广告提供具体的T
广告时,可以推断出此Func
,例如:{/ p>
GetTransactions(..., (sn, t, d, g, c, tx) => return new {
StoreNumber = sn,
Transaction = t,
Date = d,
Gross = g,
Cost = c,
Tax = tx
});
这样生成的类型在被调用方法之外可用,因为您负责将其定义给调用者。没有POCO DTO,没有动态,不变性,开箱即用的平等。看看吧。
答案 3 :(得分:0)
你确定动态太慢吗?我已经对动态操作与直接调用进行了性能测试,并且实际上对动态调用的影响很小而感到惊讶。我没有我的号码,但found this little blog post显示操作长度仅增加了十倍。他的例子显示了200万次调用需要85毫秒而静态调用需要7毫秒。
确实加起来了。六秒钟的操作需要一分钟,但它比处理纯粹的反射要少得多。