12

设想:

我目前正在编写一个层来将 3 个类似的 Web 服务抽象为一个可用的类。每个 Web 服务都公开了一组具有共同性的对象。我创建了一组利用共性的中间对象。但是在我的层中,我需要在 Web 服务对象和我的对象之间进行转换。

在调用 Web 服务之前,我使用反射在运行时创建了适当的类型,如下所示:

    public static object[] CreateProperties(Type type, IProperty[] properties)
    {
        //Empty so return null
        if (properties==null || properties.Length == 0)
            return null;

        //Check the type is allowed
        CheckPropertyTypes("CreateProperties(Type,IProperty[])",type);

        //Convert the array of intermediary IProperty objects into
        // the passed service type e.g. Service1.Property
        object[] result = new object[properties.Length];
        for (int i = 0; i < properties.Length; i++)
        {
            IProperty fromProp = properties[i];
            object toProp = ReflectionUtility.CreateInstance(type, null);
            ServiceUtils.CopyProperties(fromProp, toProp);
            result[i] = toProp;
        }
        return result;
    }

这是我的调用代码,来自我的一个服务实现:

Property[] props = (Property[])ObjectFactory.CreateProperties(typeof(Property), properties);
_service.SetProperties(folderItem.Path, props);

因此,每个服务都公开了一个不同的“Property”对象,我将其隐藏在我自己的 IProperty 接口实现后面。

反射代码在单元测试中工作,产生一个对象数组,其元素是适当的类型。但调用代码失败:

System.InvalidCastException:无法将“System.Object[]”类型的对象转换为“MyProject.Property[]”类型

有任何想法吗?

我的印象是,只要包含的对象是可转换的,来自 Object 的任何转换都会起作用?

4

6 回答 6

9

基本上,没有。数组协方差有一些有限的用途,但最好简单地知道您想要哪种类型的数组。有一个通用的 Array.ConvertAll 很容易(至少,使用 C# 3.0 更容易):

Property[] props = Array.ConvertAll(source, prop => (Property)prop);

C# 2.0 版本(含义相同)不太容易吸引眼球:

 Property[] props = Array.ConvertAll<object,Property>(
     source, delegate(object prop) { return (Property)prop; });

或者只是创建一个合适大小的新 Property[] 并手动(或通过Array.Copy)复制。

作为使用数组协方差可以做的事情的一个例子:

Property[] props = new Property[2];
props[0] = new Property();
props[1] = new Property();

object[] asObj = (object[])props;

在这里,“asObj”仍然是一个Property[]- 它可以简单地访问为object[]. 在 C# 2.0 及更高版本中,泛型通常是比数组协方差更好的选择。

于 2008-10-17T14:44:39.410 回答
9

替代答案:泛型。

public static T[] CreateProperties<T>(IProperty[] properties)
    where T : class, new()
{
    //Empty so return null
    if (properties==null || properties.Length == 0)
        return null;

    //Check the type is allowed
    CheckPropertyTypes("CreateProperties(Type,IProperty[])",typeof(T));

    //Convert the array of intermediary IProperty objects into
    // the passed service type e.g. Service1.Property
    T[] result = new T[properties.Length];
    for (int i = 0; i < properties.Length; i++)
    {
        T[i] = new T();
        ServiceUtils.CopyProperties(properties[i], t[i]);
    }
    return result;
}

然后您的调用代码变为:

Property[] props = ObjectFactory.CreateProperties<Property>(properties);
_service.SetProperties(folderItem.Path, props);

更干净:)

于 2008-10-17T14:56:13.617 回答
5

正如其他人所说,数组必须是正确的类型。其他答案已经展示了如何在事后转换真正的 object[] ,但是您可以创建正确类型的数组以使用Array.CreateInstance开始:

object[] result = (object[]) Array.CreateInstance(type, properties.Length);

假设type是引用类型,这应该可以工作 - 数组将是正确的类型,但您将使用它object[]来填充它。

于 2008-10-17T14:45:33.230 回答
4

你不能像这样转换一个数组——它返回一个对象数组,这与一个对象不同。试试Array.ConvertAll

于 2008-10-17T14:42:12.973 回答
1

没错,但这并不意味着您可以将 Object 类型的容器转换为其他类型的容器。Object[] 与 Object 不同(尽管奇怪的是,您可以将 Object[] 转换为 Object)。

于 2008-10-17T14:42:50.163 回答
1

在 C# 2.0 中,您可以使用反射来执行此操作,而无需使用泛型并且在编译时不知道所需的类型。

//get the data from the object factory
object[] newDataArray = AppObjectFactory.BuildInstances(Type.GetType("OutputData"));
if (newDataArray != null)
{
    SomeComplexObject result = new SomeComplexObject();
    //find the source
    Type resultTypeRef = result.GetType();
    //get a reference to the property
    PropertyInfo pi = resultTypeRef.GetProperty("TargetPropertyName");
    if (pi != null)
    {
        //create an array of the correct type with the correct number of items
        pi.SetValue(result, Array.CreateInstance(Type.GetType("OutputData"), newDataArray.Length), null);
        //copy the data and leverage Array.Copy's built in type casting
        Array.Copy(newDataArray, pi.GetValue(result, null) as Array, newDataArray.Length);
    }
}

您可能希望将所有 Type.GetType("OutputData") 调用和 GetProperty("PropertyName") 替换为从配置文件中读取的一些代码。

我还将使用泛型来指示 SomeComplexObject 的创建

T result = new T();
Type resultTypeRef = result.GetType();

但我说你不需要使用泛型。

不能保证性能/效率,但它确实有效。

于 2010-07-20T14:39:30.660 回答