zoukankan
html css js c++ java
泛型最佳实践C#
什么时候我不应该使用泛型?对泛型我应该使用什么命名规范?我应该在泛型接口上面添加约束吗?
如何处置(Dispose)泛型接口?可以对一般类型参数进行类型转换吗?
对泛型类如何同步多线程访问?如何序列化泛型类?
什么时候我不应该使用泛型?
不使用泛型的主要原因就是跨目标(cross
-
targeting)——如果你要在.NET
1
.1和.NET
2
.0下编译相同的代码,那么由于只有.NET
2
.0支持泛型,你就不能够使用泛型。
对泛型我应该使用什么命名规范?
我建议使用一个单独的大写字母来表示一般类型参数。如果你对类型参数没有其他的跟上下文有关的信息(additional contextual information),你应该使用字母T:
public
class
MyClass
<
T
>
{
}
在所有其他场合下,微软正式的对泛型的的命名规范指导是:
一般类型参数要有描述性的名字,除非一个单独的字母已经表示得很清楚,再增加描述性的名字也没有多大用处。
public
interface
ISessionChannel
<
TSession
>
{
}
public
delegate
TOutput Converter
<
TInput,TOutput
>
(TInput from);
可以考虑在一般类型参数的名字中表示出添加给该一般类型参数的约束。例如,一个被约束到ISession接口的参数可以起名为TSession。
我应该在泛型接口上面添加约束吗?
接口可以为其使用的范型类型添加约束,例如:
public
interface
ILinkedList
<
T
>
where T : IComparable
<
T
>
{
}
但是,你应该小心,在接口层面上定义约束还隐含有另外一层意思。为了强调接口与实现分离的思想,接口不应该包括任何一点实现的细节。虽然有很多方法可以用来实现范型接口,但是使用特定的类型参数毕竟是一种实现的细节。约束通常情况下会更加耦合(couple)接口和特定的实现。
更好的方法是,为实现范型接口的类添加约束,保持接口本身没有约束:
public
class
LinkedList
<
T
>
: ILinkedList
<
T
>
where T : IComparable
<
T
>
{
//
Rest of the implementation
}
如何处置(Dispose)泛型接口?
在C#和Visual Basic中,如果你把一个一般类型参数的对象放在using语句中,编译器无法知道客户端(client)指定的实际类型是否支持IDisposable接口。因此编译器不允许在using语句中使用一般类型参数的实例。
public
class
MyClass
<
T
>
{
public
void
SomeMethod(T t)
{
using
(t)
//
Does not compile
{
}
}
}
当然,你可以强制约束类型参数支持IDisposable接口:
public
class
MyClass
<
T
>
where T : IDisposable
{
public
void
SomeMethod(T t)
{
using
(t)
{
}
}
}
但是你不应该这么做。这样做的问题在于你不能使用接口作为类型参数了,即使这个接口的基础类型(underlying type)支持IDisposable也不行:
public
interface
IMyInterface
{}
public
class
MyOtherClass : IMyInterface,IDisposable
{
}
public
class
MyClass
<
T
>
where T : IDisposable
{
public
void
SomeMethod(T t)
{
using
(t)
{
}
}
}
MyOtherClass myOtherClass
=
new
MyOtherClass();
MyClass
<
IMyInterface
>
obj
=
new
MyClass
<
IMyInterface
>
();
//
Does not compile
obj.SomeMethod(myOtherClass);
作为替代,我建议你在using语句里对一般类型参数使用C#中的as操作符或者Visual Basic中的TryCast操作符来允许接口作为一般类型参数使用:
public
class
MyClass
<
T
>
{
public
void
SomeMethod(T t)
{
using
(t
as
IDisposable)
{
}
}
}
可以对一般类型参数进行类型转换吗?
对于隐式转换,编译器只允许将一般类型参数转换为object类型,或者其约束里指定的那个类型:
interface
ISomeInterface
{
}
class
BaseClass
{
}
class
MyClass
<
T
>
where T : BaseClass,ISomeInterface
{
void
SomeMethod(T t)
{
ISomeInterface obj1
=
t;
BaseClass obj2
=
t;
object
obj3
=
t;
}
}
这种隐式转换当然是类型安全的,因为无效的转换在编译时就会被发现。
对于显示转换,编译器允许将一般类型参数转换到任何接口,但是不能转换为类:
interface
ISomeInterface
{
}
class
SomeClass
{
}
class
MyClass
<
T
>
{
void
SomeMethod(T t)
{
ISomeInterface obj1
=
(ISomeInterface)t;
//
Compiles
SomeClass obj2
=
(SomeClass)t;
//
Does not compile
}
}
但是,你可以通过使用一个临时的object类型变量来强制将一般类型参数转到到任何其他类型:
class
MyOtherClass
{
}
class
MyClass
<
T
>
{
void
SomeMethod(T t)
{
object
temp
=
t;
MyOtherClass obj
=
(MyOtherClass)temp;
}
}
毫无疑问,这样的显示转换是很危险的,因为如果实际使用的替代一般类型参数的类型不是从你要转换到的类型那里继承的话,就可能在运行时抛出异常。
为了避免这种转换时有异常的风险,一个更好的办法是使用is或者as操作符。如果一般类型参数是(
is
)要查询的类型,
is
操作符会返回true,而as操作符会在两个类型兼容的时候执行转换,否则将返回null。
public
class
MyClass
<
T
>
{
public
void
SomeMethod(T t)
{
if
(t
is
int
)
{
}
if
(t
is
LinkedList
<
int
,
string
>
)
{
}
string
str
=
t
as
string
;
if
(str
!=
null
)
{
}
LinkedList
<
int
,
string
>
list
=
t
as
LinkedList
<
int
,
string
>
;
if
(list
!=
null
)
{
}
}
}
对泛型类如何同步多线程访问?
通常来说,你不应该在一般类型参数上应用Monitor。这是因为Monitor只能用于引用类型。当你使用范型的时候,编译器不能预先判断你将会提供一个引用类型还是值类型的类型参数。在C#中,编译器会允许你使用lock语句,但是如果你提供了一个值类型作为类型参数,lock语句在运行时将不起作用。在Visual Basic中,编译器如果不能确定一般类型参数是一个引用类型,它将不允许在一般类型参数上面使用SyncLock。
在C#和Visual Basic中,唯一你可以安全地将一般类型参数锁住的时候,是你将一般类型参数限制为引用类型,要么添加约束使其为引用类型,要么从一个基类中继承:
public
class
MyClass
<
T
>
where T :
class
{..}
public
class
SomeClass
{
}
public
class
MyClass
<
T
>
where T : SomeClass
{
}
然而,通常对于同步来说,最好避免部分地锁住单独的成员变量,因为这会增加死锁的可能性。
如何序列化泛型类?
包括了一般类型参数作为成员的范型类是可以被标记为序列化的:
[Serializable]
public
class
MySerializableClass
<
T
>
{
T m_T;
}
但是,在这种情况下,只有指定的类型参数可以被序列化时,范型类才可以被序列化。看下面的代码:
public
class
SomeClass
{}
MySerializableClass
<
SomeClass
>
obj;
obj不能被序列化,因为类型参数SomeClass不可以被序列化。因此,MySerializableClass
<
T
>
可能可以,也可能不可以被序列化,取决于使用的一般类型参数。这样可能导致在运行时丢失数据或者系统崩溃,因为客户应用程序可能不能够保持对象的状态。
目前,.NET没有提供将一般类型参数约束为可序列化的机制。解决办法是在运行时在使用这个类型之前单独进行检查,并且在任何损害发生之前马上中止使用。你可以把这个运行时的验证放在静态构造器里面:
[Serializable]
class
MySerializableClass
<
T
>
{
T m_T;
static
MySerializableClass()
{
ConstrainType(
typeof
(T));
}
static
void
ConstrainType(Type type)
{
bool
serializable
=
type.IsSerializable;
if
(serializable
==
false
)
{
string
message
=
"
The type
"
+
type
+
"
is not serializable
"
;
throw
new
InvalidOperationException(message);
}
}
}
静态构造器对每一个应用程序域的每一个类型只执行一次,而且是在类型第一次被请求实例化之前。尽管你有一些通过编程的方式来在运行时进行判断和执行检查,但是这种在静态构造器里面执行约束验证的技术,对任何无法在编译时进行检查的约束都适用。
查看全文
相关阅读:
入侵特斯拉——智能汽车安全性分析
D-Link系列路由器漏洞挖掘入门
工控安全入门之 Ethernet/IP
浅谈JS数据类型存储问题
【备忘】12306购票必杀技
制作炫酷的专题页面
杂记(下)
杂记(上)
跨域请求解决方案
好用的表单验证插件
原文地址:https://www.cnblogs.com/ghd258/p/270411.html
最新文章
Github学习笔记-不定时更新
tornado框架源码分析---Application类之debug参数
tornada模板学习笔记
.net 调用 网易云的短信验证
Redis 通用方法 存储DataTable DataRow DataSet
Redis 安装
oracle11G 已开启监听,但远程连接依旧无监听解决过程
IIS 无法访问请求的页面,因为该页的相关配置数据无效。
MVC ajaxfileupload 实现无刷新导入或上传功能
mvc jQuery 点击按钮实现导出Excel功能 参数长短不限
热门文章
禁止所有文本框输入特殊字符
Jquery 替换全部字符
Jquery获取URL参数
逆向新手踩坑指南之爬爬山能锻炼身体
Cowrie蜜罐部署教程
无聊开始玩路由器,入门Tomato固件
用Buildout来构建Python项目
Python中文转为拼音
docker到底比LXC多了些什么
CAN总线简介:如何以编程方式控制汽车
Copyright © 2011-2022 走看看