通用转换方法抛出InvalidCastException [英] Generic conversion method throw InvalidCastException

查看:138
本文介绍了通用转换方法抛出InvalidCastException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现相同的简单通用转换方法,但在运行时我收到错误。

所以这个场景很简单。我有相同的服务,返回类型为External的项目列表。我有我自己的WrapperExternal类,它只是简单地包装这个类并向它公开一些额外的功能。我有一些从WrapExternal继承的类,并添加了不同的功能。



我想创建接受外部列表项的列表并返回项目列表的通用方法

我的应用程序代码:

  static void Main( string [] args)
{
var items = GetItemsFromServer();
var converted = ConvertItems< SubWrapperExternal>(items).ToList();
}

public static IEnumerable< T> ConvertItems< T>(IEnumerable的<外部> externalItems)其中,T:WrapperExternal
{
返回externalItems
。凡(项目=>真)
。选择(项目=> (T)项);

$ / code>

当您尝试运行此代码时,您会在行(T) item:

 在ConsoleApplication1.exe中出现未处理的异常类型'System.InvalidCastException'
其他信息:
无法投射ConsoleApplication1.WrapperExternal类型的对象来键入ConsoleApplication1.SubWrapperExternal。

你知道我该怎么做才行?



测试应用程序代码:

 命名空间ConsoleApplication1 
{
类程序
{
static void Main(string [] args)
{
var items = GetItemsFromServer();

var converted = ConvertItems< SubWrapperExternal>(items).ToList();

}

private static List< External> GetItemsFromServer()
{
返回新列表< External>
{
新外部{名称=A},
新外部{名称=B},
新外部{名称=C},
};
}

public static IEnumerable< T> ConvertItems< T>(IEnumerable的<外部> externalItems)其中,T:WrapperExternal
{
返回externalItems
。凡(项目=>真)
。选择(项目=> (T)项);
}
}

class External
{
public string Name {get;组; }

}

class WrapperExternal
{
public External External {get;私人设置; }

public WrapperExternal(外部外部)
{
External = external;
}

public static显式运算符WrapperExternal(External item)
{
return item!= null?新的WrapperExternal(item):null;
}

public static implicit operator External(WrapperExternal item)
{
return item!= null? item.External:null;



class SubWrapperExternal:WrapperExternal
{
public SubWrapperExternal(External external)
:base(external)
{
}

public static explicit operator SubWrapperExternal(External item)
{
return item!= null?新的SubWrapperExternal(item):null;
}

public static implicit operator External(SubWrapperExternal item)
{
return item!= null? item.External:null;




解决方案

转换运算符与泛型一起使用时很简单 - 泛型不支持任何静态运算符重载。因此,(T) cast正在执行非转换类型检查(泛型需要对每个可能的 T ,请记住) - 一个基本的 castclass



唯一的简单你想要欺骗动态

  return externalItems.Select( item =>(T)(dynamic)item); 

由于C#特定的动态提供程序知道所有C#的常见规则,它知道转换操作符,并将按需应用它们。与此相关的性能成本略有降低,但并不像它乍看起来那么糟糕,因为该策略被缓存(如IL)每种类型一次 - 它不执行每项反射。


I want to implement same simple generic conversion method but on runtime I am getting an error.

So the scenario is quite simple. I have same service that return me list of items of type External. I have my own WrapperExternal class that simply wrap this class and expose some additional functionality to it. I have some another set of classes that inherit from WrapExternal and add different functionalities.

I want to create generic method that accept list of External list items and return list of items of specified type.

My application code:

    static void Main(string[] args)
    {
        var items = GetItemsFromServer();
        var converted = ConvertItems<SubWrapperExternal>(items).ToList();
    }

    public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
    {
        return externalItems
            .Where( item => true)
            .Select(item => (T)item);
    }

When you try to run this code you will get exception in line (T)item:

An unhandled exception of type 'System.InvalidCastException' occurred in ConsoleApplication1.exe 
Additional information: 
Unable to cast object of type 'ConsoleApplication1.WrapperExternal' to type 'ConsoleApplication1.SubWrapperExternal'.

Do you know how can I make it to works?

Test application code:

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        var items = GetItemsFromServer();

        var converted = ConvertItems<SubWrapperExternal>(items).ToList();

    }

    private static List<External> GetItemsFromServer()
    {
        return new List<External>
        {
            new External{Name = "A"},
            new External{Name = "B"},
            new External{Name = "C"},
        };
    }

    public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
    {
        return externalItems
            .Where( item => true)
            .Select(item => (T)item);
    }
}

class External
{
    public string Name { get; set; }

}

class WrapperExternal
{
    public External External { get; private set; }

    public WrapperExternal(External external)
    {
        External = external;
    }

    public static explicit operator WrapperExternal(External item)
    {
        return item != null ? new WrapperExternal(item) : null;
    }

    public static implicit operator External(WrapperExternal item)
    {
        return item != null ? item.External : null;
    }
}

class SubWrapperExternal : WrapperExternal
{
    public SubWrapperExternal(External external)
        : base(external)
    {
    }

    public static explicit operator SubWrapperExternal(External item)
    {
        return item != null ? new SubWrapperExternal(item) : null;
    }

    public static implicit operator External(SubWrapperExternal item)
    {
        return item != null ? item.External : null;
    }
}
}

解决方案

Conversion operators are a faff to use with generics - generics don't support any static operator overloads. Because of this, the (T) cast is performing a non-converting type check (generics need to use the same IL for every possible T, remember) - a basic castclass.

The only "simple" way of doing what you want is to cheat with dynamic:

return externalItems.Select(item => (T)(dynamic)item);

Since the C#-specific dynamic provider knows all the common rules of C#, it knows about conversion operators, and will apply them on-demand. There is a slight performance cost associated with this, but it isn't as bad as it looks at first glance, as the strategy is cached (as IL) once per type - it doesn't perform per-item reflection.

这篇关于通用转换方法抛出InvalidCastException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆