了解.Net Generics – Bank Domain

这是尝试学习generics(使用.Net 4.0)。 我已经编程了大约4。5年。 直到现在我还没有在实时项目中使用generics。 我一直在做的是阅读一些关于generics的文章并尝试理解它。 问题是 – 他们中的大多数试图解释generics的各种语法。 他们用例如方形,圆形和形状的例子来解释。

现在我有机会设计一个小应用程序。 我想在那里使用generics。 [我确实看到generics在我的新项目中成为一个好候选人的机会很大]

我现在想出的是来自Bank域的一个例子,旨在理解generics。 我想了解以下4。

1)通用类

2)通用方法

3)通用接口

4)通用代表

编辑:类型独立的操作是generics的良好候选者。 这是我在以下示例中遗漏的最重要的一点。

我为“通用类”创建了一个示例。 你能帮忙用银行域名帮助其他三个项目的简单例子吗?

注意:在使用Generic类时,我发现它在Open-Closed Principle中有所帮助。 即使我添加新帐户类型,generics类也需要更改。 不断变化的逻辑(利息计算)进入特定的类。

注意:在下面,语法可能不正确,因为它在没有Visual Studio的情况下键入它。 但这个概念很好。

编辑: “AccountManager”会根据其角色成为“BankAccount”类的更好名称吗? 这是什么样的反模式?

通用类 – 具有Bank域的示例

Public Interface IBankAccount { Public int Interest; Public Int DepositedAmount; Public int DurationInMonth; } Public class FixedAccount: IbankAccount { Public int Interest { Get { Return (DurationInMonth*0.5) } } Public Int DepositedAmount {get;set}; Public int DurationInMonth {get;set}; } Public class SavingsAccount: IbankAccount { Public int Interest { Get { Return ((DurationInMonth/2)*0.1) } } Public Int DepositedAmount {get;set}; Public int DurationInMonth {get;set}; } Public Class BankAccount Where T: IbankAccount { T account = new T(); Public void CreateAccount(int duration, int amount) { account. DurationInMonth = duration; account. DepositedAmount = amont; int interestVal = account. Interest; SaveToDatabase (T); } } 

读:

  1. 什么时候适合使用generics与inheritance?

  2. generics与inheritance(当不涉及集合类时)

  3. https://codereview.stackexchange.com/questions/8797/how-to-make-sure-that-this-code-conforms-to-open-close-principle

  4. 满足开放/封闭原则的工厂模式?

  5. 我在使用generics和使用C#进行转换时遇到了一些麻烦

  6. 决定何时何地使用genericshttp://en.csharp-online.net/CSharp_Generics_Recipes -Deciding_When_and_Where_to_Use_Generics_Problem

  7. 通过generics与多态性重用代码

  8. 并行inheritance链中的多态性和类型安全性

  9. 使用C#generics扩展?

  10. C#generics和多态:一个矛盾?

只是我的两分钱,因为@Lijo让我在这里发表评论。

我会回答上面的大部分答案。 但总而言之,generics是行为的无类型重用。 您的generics类型必须是IBankAccount – 这是一个非常具体的接口 – 这说明这可能是不对的。 我并不是说您不能对接口使用限制,但该接口本身就是一个非常通用的接口,例如IDisposableIConvertible

因为“我想使用generics”通常是一个糟糕的主意。 使用合适的工具来完成正确的工作。 现在,尝试学习新东西的道具。

说…基本上,“generics”是一种指定方法,类(等)而不指定基础类型的方法。 这是将算法与数据类型分开的好方法。

例如,采用Swap方法。 基本上,无论操作类型如何,交换算法都是相同的。 所以,这将是一个通用的好候选者(如ListDictionary等)

所以,一个int的交换就像这样:

 void Swap(ref int left, ref int right) { int temp = left; left = right; right = temp; } 

现在,你可以为你的其他数据类型( floatdouble float等)写入重载,或者你可以使它成为generics并写一次,这样它几乎适用于所有数据类型:

 void Swap<_type>(ref _type left, ref _type right) { _type temp = left; left = right; right = temp; } 

现在,您的示例代码不起作用:

 Public void CreateAccount(int duration, int amount) { T.DurationInMonth = duration; T.DepositedAmount = amont; int interestVal = T.Interest; SaveToDatabase (T); } 

这里, T是类型,而不是对象的实例。 如果你替换T ,它会变得更清楚:

 Public void CreateAccount(int duration, int amount) { IbankAccount.DurationInMonth = duration; IbankAccount.DepositedAmount = amont; int interestVal = IbankAccount.Interest; SaveToDatabase (IbankAccount); } 

当你真正想要的是这个:

 Public void CreateAccount(int duration, int amount) { account.DurationInMonth = duration; account.DepositedAmount = amont; int interestVal = account.Interest; SaveToDatabase (account); } 

你看,我们在这里调用类帐户的INSTANCE方法,而不是通用的IbankAccount TYPE

generics是关于generics类型参数。 如果你想编写一些东西而你不知道它将提前应用哪种类型,你会声明一个generics类型参数。

 class MyStore { } 

这里T是generics类型参数。 您不知道它代表的是哪种类型。

你可以写这样的东西

 class MyStore { public void Store(T item) { ... } public T Retrieve() { ... } } 

现在您可以像这样使用MyStore

 var stringStore = new MyStore(); stringStore.Store("Hello"); string s = stringStore.Retrieve(); var intStore = new MyStore(); intStore.Store(77); int i = intStore.Retrieve(); 

你也可以像这样申报商店; 但是,它不是类型安全的

 class MyStore { public void Store(object item) { ... } public object Retrieve() { ... } } 

你必须投射结果

 var stringStore = new MyStore(); stringStore.Store("Hello"); string s = (string)stringStore.Retrieve(); var intStore = new MyStore(); intStore.Store(77); int i = (int)intStore.Retrieve(); var doubleStore = new MyStore(); doubleStore.Store("double"); double d = (double)doubleStore.Retrieve(); // OOPS! A runtime error is generated here! 

0)将.NET的通用集合与您的类型用作容器类型:假设您要列出与特定客户关联的所有帐户,并将其显示在数据网格中。 如果您想查看多个客户的帐户, List会有所帮助,或者Dictionary

1,2)创建自己的generics类和generics方法 :假设您要执行涉及所有帐户的计算以生成报告。 在这个特定的报告中,数值精度并不重要,速度也是如此。 所以你可以使用Single insted of Decimal 。 在这种特殊情况下,为了使计算中涉及的类与所使用的数字类型无关,使用generics参数比inheritance更自然。 伪代码:

 public class MetricCalculator{ private bool _dirty; private T _cachedValue; T PerformCalculation(){ if( !_dirty ) return cachedValue; T metric = 0; foreach( IBankAccount account in AccountMapper.GetAll() ){ T += account.Foo * accound.Bar; } _cachedValue = metric; return metric; } } 

在此示例中,MetricCalculator是一个generics类,因为其中一个数据成员是参数化类型。 如果使用的值未更改,则该成员用于避免重复计算。 还有一种通用方法,可以在不关心所使用的数字类型的情况下执行计算。 如果不需要缓存值,则可以只使用通用方法的公共类。 我结合两个只是为了节省空间。

3)通用接口:假设您想要完全解耦所有组件(例如,实现控制反转 ); 在这种情况下,如果您使用跨程序集使用的MetricCalculator等generics类,则需要通过通用接口使用它们。 另一个例子是如果你需要编写自定义数据结构或迭代器,但我怀疑你是否必须这样做。

4)通用事件:回到MetricCalculator示例,假设您要通过一个事件通知某个观察者对象,该事件通知计算已完成,并传递结果。 这将是一个常见的事件,但你在提升事件时会传递一个类型为T的参数。 注意 :如果可用,最好使用C#5的async-awaitfunction。