首页 > 编程 > .NET > 正文

.NET泛型技巧之类型参数之间的转换

2024-07-10 13:11:57
字体:
来源:转载
供稿:网友
  首先我要介绍的技巧是如何提供类型参数之间的转换。我们知道,.net泛型将每个类型参数理解为一个独立的类型。如果不通过约束指定,编译器不会对类型参数所代表的类型做任何假设。也就是说,如果在某个上下文中有两个不同的类型参数u和v,编译器不会知道运行时他们代表的真实类型能否进行类型转换,因此会拒绝编译如下代码:

public function genericcast(of u, v)(byval obj as u) as v
 return ctype(obj, v)
end function


  除非我们加以约束u是v的子类。这显然是为了类型安全做出的考虑,因为.net这种跨程序集的泛型无法在编译时做出足够的检查来确保类型安全。但是限制了这种操作,就妨碍了我们做事,很多代码因此写不出来。于是,我们可以利用.net所有类型的基类object来绕过这一限制:

return directcast(directcast(obj, object), v)


  vb的directcast运算符在泛型的类型参数上作用与c#的括号运算符相同。也就是说,这段代码用c#写起来是这样:

return (v)(object)obj;


  这样,即使不约束u和v之间的关系,这段代码也能编译了。但是他的功能却不能令我们满意。这样写出的类型转换实际上还是仅当u是v本身或其子类的时候才能转换成功。而其他一切情况都会转换失败。不管u和v的运行时类型之间是否定义有其他类型转换规则。这显然不合我们意愿,我们希望int与double之间的转换等语言内置的类型转换都能够自动进行,否则就和约束没什么两样了。对vb用户来说有一个极为简单的解决方案——把第二次directcast变成ctype:

public function genericcast(of u, v)(byval obj as u) as v
 return ctype(directcast(obj, object), v)
end function


  现在,genericcast泛型方法就能执行int与double等内置规则的转换了。很神奇?因为ctype运算符在编译时自动调用了vb运行库的转换函数,该函数在运行期间对泛型类型参数的真实类型做了检查。而且这个过程的性能完全可以接受。

  现在vb的用户已经轻松享受这一功能了。但是c#的事情还没完,因为c#没有如此智能的类型转换运算符,因此就需要手工实现vb运行库所代办的那些任务。其实就是利用了一下iconvertible:

static v genericcast<u, v>(u obj)
{
 iconvertible convertibleobj = obj as iconvertible;
 if (convertibleobj != null)
 {
  type t = typeof(v);

  switch (type.gettypecode(t))
  {
   case typecode.boolean:
    return (v)(object)convertibleobj.toboolean(null);
   case typecode.byte:
    return (v)(object)convertibleobj.tobyte(null);
   case typecode.char:
    return (v)(object)convertibleobj.tochar(null);

   //.........
   default:
    //none of them, use the following default way..
    break;
  }
 }
 return (v)(object)obj;
}


  不过这个方法看起来不但很丑、很麻烦,功能上还达不到vb版。所以建议您用到这种类型参数之间的转换,就用vb封装这一功能,然后做成dll供c#调用吧。

  经过研究发现convert.changetype方法已经封装了iconvertible判断的那些代码,因此c#版可采用这种写法:

static v genericcast<u, v>(u obj)
{
 return (v)convert.changetype(obj, typeof(v));
}


  不过它仍然没有vb版的ctype功能丰富。此外我还发现了vb运行库的changetype还有支持用户自定义类型转换运算符的功能。所以终极版本如下:

public function genericcast(of u, v)(byval obj as u) as v
try
 return ctype(directcast(obj, object), v)
 catch ex as invalidcastexception return directcast( _
  compilerservices.conversions.changetype(obj, gettype(v)), v)
 end try
end function

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表