这是尝试学习泛型(使用.Net 4.0)。我已经编程了大约4。5年。直到现在我还没有在实时项目中使用泛型。我一直在做的是阅读一些关于泛型的文章并尝试理解它。问题是 - 他们中的大多数试图解释泛型的各种语法。他们用例如方形,圆形和形状的例子来解释。
现在我有机会设计一个小应用程序。我想在那里使用泛型。 [我确实看到Generics在我的新项目中成为一个好候选人的机会很大]
我现在想出的是来自Bank域的一个例子,旨在理解泛型。我想了解以下4个。
1)通用类
2)通用方法
3)通用接口
4)通用代表
编辑:独立类型的操作是泛型的良好候选者。这是我在以下示例中遗漏的最重要的一点。
我为“通用类”创建了一个示例。你能帮忙用银行域名帮助其他三个项目的简单例子吗?
注意:在使用Generic类时,我发现它在开放 - 封闭原则中有所帮助。即使我添加新的帐户类型,通用类也需要更改。变化的逻辑(利息计算)进入特定的类。
注意:在下文中,语法可能不正确,因为它在没有Visual Studio的情况下键入它。但这个概念很有用。
编辑: Will" AccountManager"是" BankAccount"更好的名字基于它的角色?这是什么样的反模式?
通用类 - 银行域示例
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<T> 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);
}
}
读:
Generics vs inheritance (when no collection classes are involved)
A Factory Pattern that will satisfy the Open/Closed Principle?
决定何时何地使用泛型 http://en.csharp-online.net/CSharp_Generics_Recipes - Deciding_When_and_Where_to_Use_Generics_Problem
答案 0 :(得分:3)
说... 基本上,“泛型”是一种在编写方法时指定方法,类(等)而不指定基础类型的方法。这是将算法与数据类型分开的好方法。
例如,采用Swap方法。基本上,无论操作类型如何,交换算法都是相同的。所以,这将是通用的一个很好的候选者(如List
,Dictionary
等等。
所以,int
的交换是这样的:
void Swap(ref int left, ref int right)
{
int temp = left;
left = right;
right = temp;
}
现在,您可以为其他数据类型(float
,double
等编写重载
或者你可以使它成为通用的并写一次,这样它几乎适用于所有数据类型:
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
答案 1 :(得分:1)
0)将.NET的通用集合与您的类型用作容器类型:假设您要列出与特定客户关联的所有帐户,并将其显示在数据网格中。如果您想要查看多个客户的帐户,List<IBankAccount>
会有所帮助,或Dictionary<ICustomerID,IBankAccount>
。
1,2)创建自己的泛型类和泛型方法:假设您要执行涉及所有帐户的计算以生成报告。在这个特定的报告中,数值精度并不重要,速度也是如此。因此,您可以Single
使用Decimal
。在这种特殊情况下,为了使计算中涉及的类独立于所使用的数字类型,使用泛型参数比继承更自然。伪代码:
public class MetricCalculator<T>{
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是一个泛型类,因为其中一个数据成员属于参数化类型。如果使用的值未更改,则该成员用于避免重复计算。还有一种通用方法,可以在不关心所使用的数字类型的情况下执行计算。如果不需要缓存该值,则可以只使用泛型方法的公共类。我把两者结合起来只是为了节省空间。
3)通用接口:假设您要完全解耦所有组件(例如,实现Inversion of Control);在这种情况下,如果您使用跨程序集使用的MetricCalculator
等泛型类,则需要通过通用接口使用它们。另一个例子是如果你需要编写一个自定义数据结构或迭代器,但我怀疑你不得不这样做。
4)通用事件:返回MetricCalculator
示例,假设您要通过一个事件通知某个观察者对象,该事件通知计算已完成,并传递结果。它就像一个常见的事件,但在提升事件时你会传递T
类型的参数。 注意:如果可用,最好使用C#5的async-await功能。
答案 2 :(得分:1)
泛型是关于泛型类型参数。如果你想编写一些东西而你不知道它将提前应用哪种类型,你会声明一个泛型类型参数。
class MyStore<T>
{
}
此处T
是泛型类型参数。你不知道它代表的是哪种类型。
你可以写这样的东西
class MyStore<T>
{
public void Store(T item)
{
...
}
public T Retrieve()
{
...
}
}
现在您可以像这样使用MyStore
:
var stringStore = new MyStore<string>();
stringStore.Store("Hello");
string s = stringStore.Retrieve();
var intStore = new MyStore<int>();
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!
答案 3 :(得分:1)
只是我的两分钱,因为@Lijo让我在这里发表评论。
我会选择上述大部分答案。
但总而言之,泛型是无类型重用行为。您的泛型类型必须是IBankAccount
- 这是一个非常具体的接口 - 这说明这可能是不对的。我并不是说您不能对接口使用限制,但该接口本身就是一个非常通用的接口,例如IDisposable
或IConvertible
。