【发布时间】:2014-10-02 09:12:27
【问题描述】:
我知道这个主题被讨论过很多次: What are the good reasons to wish that .NET generics could inherit one of the generic parameter types?
但现在我认为它确实需要......
我将尝试用简单的例子来解释为什么我们需要从泛型类型继承。
简而言之,动机是让我称之为Compile Time Order Enforcement的东西的开发变得更加容易,这在ORM框架中非常流行。
假设我们正在构建数据库框架。
以下是如何使用这样的框架构建事务的示例:
public ITransaction BuildTransaction(ITransactionBuilder builder)
{
/* Prepare the transaction which will update specific columns in 2 rows of table1, and one row in table2 */
ITransaction transaction = builder
.UpdateTable("table1")
.Row(12)
.Column("c1", 128)
.Column("c2", 256)
.Row(45)
.Column("c2", 512)
.UpdateTable("table2")
.Row(33)
.Column("c3", "String")
.GetTransaction();
return transaction;
}
由于每一行都返回了一些接口,所以我们希望这样返回,开发者不会弄错操作顺序,并且在编译时会强制执行有效的使用,这也使得实现和使用更简单TransactionBuilder,因为开发者不能犯这样的错误:
{
ITransaction transaction = builder
.UpdateTable("table1")
.UpdateTable("table2") /*INVALID ORDER: Table can't come after Table, because at least one Row should be set for previous Table */
}
// OR
{
ITransaction transaction = builder
.UpdateTable("table1")
.Row(12)
.Row(45) /* INVALID ORDER: Row can't come after Row, because at least one column should be set for previous row */
}
现在让我们看看今天的 ITransactionBuilder 接口,没有从泛型继承,它将在编译时强制执行所需的顺序:
interface ITransactionBuilder
{
IRowBuilder UpdateTable(string tableName);
}
interface IRowBuilder
{
IFirstColumnBuilder Row(long rowid);
}
interface IFirstColumnBuilder
{
INextColumnBuilder Column(string columnName, Object columnValue);
}
interface INextColumnBuilder : ITransactionBuilder, IRowBuilder, ITransactionHolder
{
INextColumnBuilder Column(string columnName, Object columnValue);
}
interface ITransactionHolder
{
ITransaction GetTransaction();
}
interface ITransaction
{
void Execute();
}
如您所见,我们有 2 个用于 Column builder “IFirstColumnBuilder” 和 “INextColumnBuilder” 的接口,这实际上是不必要的,请记住,这是一个非常简单的编译时状态机示例,而在更复杂的问题中,不必要的接口将急剧增加。
现在假设我们可以从泛型继承并准备这样的接口
interface Join<T1, T2> : T1, T2 {}
interface Join<T1, T2, T3> : T1, T2, T3 {}
interface Join<T1, T2, T3, T4> : T1, T2, T3, T4 {} //we use only this one in example
然后,我们可以用更直观的样式和单列构建器重写我们的界面,并且不影响顺序
interface ITransactionBuilder
{
IRowBuilder UpdateTable(string tableName);
}
interface IRowBuilder
{
IColumnBuilder Row(long rowid);
}
interface IColumnBuilder
{
Join<IColumnBuilder, IRowBuilder, ITransactionBuilder, ITransactionHolder> Column(string columnName, Object columnValue);
}
interface ITransactionHolder
{
ITransaction GetTransaction();
}
interface ITransaction
{
void Execute();
}
所以我们使用 Join<...> 来组合现有接口(或“下一步”),这在状态机开发中非常有用。
当然,这个具体问题可以通过在 C# 中添加“加入”接口的可能性来解决,但是很明显,如果可以从泛型继承,则根本不存在问题,而且很明显编译时间顺序执行是非常有用的东西。
顺便说一句。对于像
这样的语法 interface IInterface<T> : T {}
除了继承循环之外,没有任何“假设”情况,可以在编译时检测到。
我认为至少对于接口来说,这个特性是 100% 需要的
问候
【问题讨论】:
-
你的问题到底是什么?
-
我明白你的意思。没有不好的感觉,但是,我认为这篇文章与 SO 无关。根据网站的使用条款,您应该提出一个可以提供有效解决方案的可挽救问题。您的帖子更像是对 CLR 开发团队的功能请求,我怀疑您是否会在本网站范围内以及您撰写的帖子的生命周期内获得可行的解决方案。
-
其实我只是想听听其他人对我的想法有何看法?我是 c# 新手,大约 4 周,来自 c++ 世界。
-
我不确定减少大型流畅接口所需的接口数量实际上是添加如此复杂(在实现方面)功能的一个很好的理由。如果您真的想这样做,请使用一些元编程方法(例如 T4)从 DSL 生成流畅的接口。
标签: c# templates generics inheritance