2008-10-17 1 views
10

Сценарий:Невозможно передать объект типа 'System.Object []' в 'MyObject []', что дает?

В настоящее время я пишу слой для абстрактного 3 похожих веб-сервисов в один пригодный для использования класс. Каждый веб-сервис предоставляет набор объектов, которые обмениваются общими. Я создал набор промежуточных объектов, которые используют общность. Однако в моем слое мне нужно преобразовать объекты веб-службы и мои объекты.

Я использовал отражение для создания соответствующего типа во время выполнения, прежде чем я сделать вызов веб-службы, как так:

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); 

Таким образом, каждая служба предоставляет другой объект «Свойство», который я скрываю за моей собственной реализацией моего интерфейса IProperty.

Код отражения работает в модульных тестах, создавая массив объектов, элементы которых имеют соответствующий тип. Но вызывающий код не удается:

System.InvalidCastException: Невозможно отлитого объекта типа 'System.Object []' ввести «MyProject.Property []

Любые идеи?

У меня создалось впечатление, что любое действие из объекта будет работать до тех пор, пока объект, содержащий объект, может быть конвертируемым?

ответ

9

Альтернативный ответ: generics.

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); 

Гораздо чище :)

4

Вы не можете преобразовать такой массив - он возвращает массив объектов, который отличается от объекта. Попробуйте Array.ConvertAll

1

Это правильно, но это не значит, что вы можете бросать контейнеры типа Object в контейнеры других типов. Объект [] - это не то же самое, что Object (хотя вы, как ни странно, могли бы использовать Object [] для Object).

+0

Ну, чтобы быть педантичным, и Object [] * есть * Объект ... так что актер не такой уж странный. Все это объект в .NET. – 2008-10-17 14:50:17

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; }); 

Или просто создайте новое свойство [] нужного размера и скопируйте его вручную (или через 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 и выше дженерики обычно делают лучший вариант, чем ковариация массива.

+0

Спасибо, это работает. Каково влияние производительности на конвертацию и копию? – 2008-10-17 14:47:32

+0

ConvertAll будет включать вызов делегата каждый раз, где - поскольку Copy должен просто использовать Buffer.BlockCopy под обручем. Так что копия должна быть быстрее. ConvertAll легче набирать (и отлично подходит для малых и средних размеров) и более гибко: вы можете делать нетривиальные прогнозы. – 2008-10-17 14:49:24

+0

Также - ConvertAll будет справляться с преобразованиями, а не с кастами; Array.Copy не может этого сделать, поскольку нет гарантии, что исходный и целевой объекты имеют одинаковый размер (что необходимо для Buffer.BlockCopy) – 2008-10-17 14:52:50

5

Как уже говорили другие, массив должен иметь нужный тип для начала.Другие ответы показали, как преобразовать подлинный объект [] после того, как тот факт, но вы можете создать правильный вид массива, чтобы начать с помощью Array.CreateInstance:

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

Предполагая type является ссылочным типом, это должно работать - массив будет иметь правильный тип, но вы будете использовать его как object[], чтобы заполнить его.

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(); 

Но я сказал, что не нужно использовать дженерики.

Отсутствие гарантий по производительности/эффективности, но оно действительно работает.