2010-10-12 63 views
1
using System; 

static class Utility<T, TReturn> 
{ 
public static TReturn Change(T arg) 
{ 
    // is there any solution to do this type casting but without dynamic keyword? 
    return (TReturn)(arg as dynamic); 
} 
} 


    class Program 
{ 
    static void Main(string[] args) 
{ 
    int i = 100; 

    try 
    { 
     short s = Utility<int,short>.Change(i); 
     Console.WriteLine(s); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex); 
    } 
} 
} 
+0

除了演员之外,你还想达到什么目的?你是否想避免为上面的例子写'(short)i'? – shahkalpesh 2010-10-12 03:59:18

+0

这只是一个简单的例子。 – xport 2010-10-12 04:03:12

回答

3

绝对如此。在这里,你去...

static class Utility<T, TReturn> { 
     public static TReturn Change(T arg) { 
      return (TReturn)Convert.ChangeType(arg, typeof(TReturn)); 
     } 
    } 
+0

哇,它的作品! – xport 2010-10-12 03:59:40

+0

@ mattmc3,相比动态,哪一个更好? – xport 2010-10-12 04:08:44

+0

恕我直言,你应该尽可能避免“动态”。动态类型在.Net 4中是新增的,并且删除了编译时类型检查,如果您习惯于输入安全性,则会使其成为危险地区。 – 2010-10-12 05:38:24

0
static class Utility<T, TReturn> 
    where T : TReturn 
{ 
    public static TReturn Change(T arg) 
    { 
     return (TReturn) arg; 
    } 
} 
+0

您的代码不适用于提问者提供的代码。 'int'不是'short'。提问者正在寻找一种类型转换,而不仅仅是一个简单的转换。 – mattmc3 2010-10-12 04:05:32

0

您可以添加约束T : TReturn。但是这不适用于您的值类型:

using System; 

static class Utility<T, TReturn> 
    where T : TReturn 
{ 
    public static TReturn Change(T arg) 
    { 
     return (TReturn)(arg); 
    } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     string i = "100"; 

     try 
     { 
      object s = Utility<string, object>.Change(i); 
      Console.WriteLine(s); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 
    } 
} 
0

即使简单:

static class Utility<T, TReturn> 
{ 
    static TReturn Change(T arg, Converter<TReturn, T> convert) 
    { 
     System.Diagnostics.Debug.Assert(arg != null); 
     return convert(arg); 
    } 
} 

或者试试下面的,如果你不能接受一个自定义转换器的委托:

static class Utility<T, TReturn> 
{ 
    static TReturn Change(T arg) 
    { 
     return (TReturn) System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)).ConvertTo(arg, typeof(TReturn)); 
    } 
} 
1

使用Convert.ChangeType(object,Type)的解决方案是不与使用动态调度的方法一样灵活。主要是因为很少有类型实现IConvertible。例如,假设你想从一个值类型转换为它的可空值类型,这会抛出一个异常。 下面是一个例子::

Converter<short, short?> test1 = @short =>(short?)Convert.ChangeType(@short, typeof(short?)); 
test1(5); 

这是不通用的代码,但它仍然证明了它会炸掉点。更糟糕的是,在这种情况下,简单的演员实际上工作。你真正需要的是一个“智能”转换器。

首先,我们需要定义您愿意接受的惩罚。 如果您准备好调用DLR,这可能是您最安全的选择。该代码几乎保证做正确的事情。由于呼叫站点被缓存,如果调用足够的话,它实际上会表现得非常好。

我不会假装没有简单的解决方案。你最好的选择是开始考虑限制功能的范围或范围。例如,如果您将TResult的限制添加为IConvertible,那么您不必担心可空。尽管如此,它仍然会炸毁Enums。

你可以做的是建立一个转换器委托来处理返回类型的工作。然后按返回类型缓存它。您将始终禁用值类型,但其内存密集程度要低很多,因此必须缓存该方法调用的每个配对的委托。该逻辑基本上必须处理几种情况::

If(TReturn.IsValueType) 
{ 
     If(TReturn.IsEnum) Cast to Enum base type using Convert.To(base), then to Enum. 
     If(TReturn.IsNullable<>) Cast to Nullable<> generic type using Convert.To(base), then to Nullable<T> 
     If(TReturn.ISNullableEnum) Cast to Enum base type using Convert.To(base), then to Nullable enum. 
     Otherwise, just call Convert.To(TReturn) if that method exists. 
} 
If it's Iconvertible try calling Convert.ChangeType(object,Type) and cast as TReturn. 
If no method has been found yet, try doing an explicit cast TReturn. 

这对实现转换运算符的代码仍然失败,这不是IConvertible。但它非常接近你想要的。要添加对转换运算符的支持,您必须为每个TInput,TReturn缓存不同的委托。这是一个更大的混乱,因为你现在开始担心在某些情况下试图不加框,在那里它更容易调用拳击方法,并且需要很多工作才能得到正确的结果。除非这是必须成为代码库的一部分,否则我会避免使用这种清理工具,return (TReturn)(value as dynamic)

我为我的数据库层做了这样的事情,我目前正在尝试用更简单和不那么痛苦的动态代码替换.NET 3.5代码。 上述可能会比DLR代码略微(并且略微)执行得更好,并且不会像DLR呼叫那样安全或稳健。

+0

谢谢。很好的解释。 – xport 2010-10-12 05:50:10