Есть ли способ привести объект обратно к исходному типу без указания каждого случая?

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

В настоящее время я делаю что-то вроде

for ( i=0;i<tmpArrayList.Count;i++)
{
   object x=tmpArrayList[i];
   if (x.GetType() ==  typeof(byte))
   {
      wrt.Write((byte)x);
   }
   ........

Проблема в том, что если пропустить их тип, мой код может сломаться в будущем.

Я хотел бы сделать что-то вроде.

object x=tmpArrayList[i];
wrt.Write(x);

но это не сработает, если я не выполню каждый актерский состав.

Редактировать:

Посмотрев ответы, я пришел к этой функции. Для тестирования эта функция отправляет массив в системный журнал.

  private void TxMsg(ArrayList TxArray,IPAddress ipaddress)
  {
     Byte[] txbuf=new Byte[0];
     int sz=0;

     // caculate size of txbuf
     foreach (Object o in TxArray)
     {
        if ( o is String ) 
        {
           sz+=((String)(o)).Length;
        }
        else if ( o is Byte[] )
        {
           sz+=((Byte[])(o)).Length;
        }
        else if ( o is Char[] )
        {
           sz+=((Char[])(o)).Length;
        }
        else // take care of non arrays
        {
           sz+=Marshal.SizeOf(o);
        }
     }
     txbuf = new Byte[sz];

     System.IO.MemoryStream stm_w = new System.IO.MemoryStream( txbuf, 0,txbuf.Length);
     System.IO.BinaryWriter wrt = new System.IO.BinaryWriter( stm_w );

     foreach (Object o in TxArray)
     {
        bool otypefound=false;
        if (o is String) // strings need to be sent one byte per char
        {
           otypefound=true;
           String st=(String)o;
           for(int i=0;i<st.Length;i++)
           {
              wrt.Write((byte)st[i]);
           }
        }
        else
        {
           foreach (MethodInfo mi in typeof(BinaryWriter).GetMethods())
           {
              if (mi.Name == "Write")
              {
                 ParameterInfo[] pi = mi.GetParameters();
                 if ((pi.Length == 1)&&(pi[0].ParameterType==o.GetType()))
                 {
                    otypefound=true;
                    mi.Invoke(wrt, new Object[] { o });
                 }
              }
           }
        }
        if(otypefound==false)
        {
           throw new InvalidOperationException("Cannot write data of type " + o.GetType().FullName);
        }
     }
     IPEndPoint endpoint = new IPEndPoint(ipaddress, 514); //syslog port
     UdpClient udpClient_txmsg = new UdpClient();
     udpClient_txmsg.Send(txbuf, txbuf.Length,endpoint); // send udp packet to syslog             
  }
11.12.2008 17:01:34
6 ОТВЕТОВ
РЕШЕНИЕ

Вот решение для BinaryWriter, которое использует отражение.

Это в основном сканирует BinaryWriter для методов с именем Write, которые принимают ровно один параметр, а затем создает словарь, метод которого обрабатывает, какого типа, а затем для каждого объекта для записи находит нужный метод и вызывает его для средства записи.

Грязный, и вы, вероятно, должны искать лучшие способы сделать все это (не только часть письма), но это должно работать для ваших текущих потребностей:

using System.IO;
using System;
using System.Reflection;
using System.Collections.Generic;
namespace ConsoleApplication14
{
    public class Program
    {
        public static void Main()
        {
            Dictionary<Type, MethodInfo> mapping = new Dictionary<Type, MethodInfo>();
            foreach (MethodInfo mi in typeof(BinaryWriter).GetMethods())
            {
                if (mi.Name == "Write")
                {
                    ParameterInfo[] pi = mi.GetParameters();
                    if (pi.Length == 1)
                        mapping[pi[0].ParameterType] = mi;
                }
            }

            List<Object> someData = new List<Object>();
            someData.Add((Byte)10);
            someData.Add((Int32)10);
            someData.Add((Double)10);
            someData.Add((Char)10);
            someData.Add("Test");

            using (FileStream file = new FileStream(@"C:\test.dat", FileMode.Create, FileAccess.ReadWrite))
            using (BinaryWriter writer = new BinaryWriter(file))
            {
                foreach (Object o in someData)
                {
                    MethodInfo mi;
                    if (mapping.TryGetValue(o.GetType(), out mi))
                    {
                        mi.Invoke(writer, new Object[] { o });
                    }
                    else
                        throw new InvalidOperationException("Cannot write data of type " + o.GetType().FullName);
                }
            }
        }
    }
}
3
11.12.2008 18:07:15
Однажды у меня была та же проблема, и я сделал это по-твоему с первой попытки, но это было слишком медленно. Переключение на лестницу if-else будет значительно быстрее!
Autodidact 19.02.2009 18:44:54

Нет. Приведение должно быть известно во время компиляции, но фактический тип известен только во время выполнения.

Обратите внимание, однако, что есть лучший способ тестирования типа, вызывающего GetType. Вместо:

if (x.GetType() == typeof(byte))

Использование:

if (x is byte)

РЕДАКТИРОВАТЬ: Чтобы ответить на дополнительные вопросы:

"Какие бывают типы?" Ну, посмотри документы для BinaryWriter, я думаю ...

"Мне нужно беспокоиться о байте и байте?" Нет, байт является псевдонимом для System.Byte в C #. Они одного типа.

7
11.12.2008 17:17:58
+1 для «просмотра документов для BinaryWriter» ... обратите внимание, что есть перегрузки для массивов - Write (char []) и Write (byte [])
Jimmy 11.12.2008 17:45:09
Почему лучше использовать GetType () для сравнения типов?
sthay 21.12.2008 04:23:25

Джон прав, но у меня была еще одна мысль, которая может оказаться полезной. Рассматривали ли вы добавление еще одного байта для передачи каждого объекта, а затем использование этого байта в качестве кода типа, говорящего вам, к чему его приводить на другом конце?

3
11.12.2008 17:09:30

То, что вы просите, - это Dynamic Dispatch , а в C # 3.0 его нет.

Вы должны по крайней мере использовать проверку во время выполнения, чтобы убедиться, что вы не пропустили тип.

Вы можете сделать что-то умное, если у вас есть, Dictionaryкоторый сопоставляет типы с функциями обработки. Вы можете заполнить отображение для всех функций обработки в одном месте. У вас больше шансов сделать это правильно, чем если бы вы написали переключатель везде, где происходит обработка.

2
11.12.2008 17:15:33
Не могу дождаться C # 4.0. Возможно, вам следует показать ему пример того, как это можно сделать в C # 4.0, используя ключевое слово dynamic?
Llyle 11.12.2008 17:20:31
Я не знаю, можно ли это сделать в C # 4.0. На самом деле, я подозреваю, что OP нужна двойная динамическая диспетчеризация , и что C # 4.0 добавит только одну динамическую диспетчеризацию . Но я не уверен.
Jay Bazuzi 14.12.2008 00:51:44

Это тот случай, когда нужно что-то под названием Double Dispatch .

Я собираюсь предположить, что объект wrt - это тот, который вы написали сами (скажем, он имеет тип Writer). Вот что вы могли бы сделать:

class Writer
{
    void write(byte b)
    {
        // write bytes here
    }

    void write(Writable something)
    {
        something.writeOn(this);
    }
}

interface Writeable
{
    void writeOn(Writer writer);
}

class SomeObject implements Writeable
{
    private Object someData;
    private Object moreData;

    void writeOn(Writer writer)
    {
        writer.write(convertToByte(someData));
        writer.write(convertToByte(moreData));
    }
}

class AnotherObject implements Writeable
{
    private int x;
    private int y;
    private int z;

    void writeOn(Writer writer)
    {
        writer.write((byte)x);
        writer.write((byte)y);
        writer.write((byte)z);
    }
}

Что делает Writer, так это отправляет обратно на вход, говоря ему использовать его (Writer) для записи, однако это делается для этого объекта, который не может быть известен заранее.

1
11.12.2008 17:29:38

Рассматривали ли вы использовать BinaryFormatter вместо BinaryWriter?

преимущества

  • Вы можете передать объект s (то есть что угодно), так что это решит вашу проблему приведения.
  • Автоматическое управление типами (фактически записывает заголовки типов в поток).
  • Поддерживает сложные ссылочные типы, а также.

Недостатки

Использует сериализацию внутри, поэтому:

  • Наверное, медленнее.
  • Поток байтов становится больше (из-за типов заголовков).
  • У вас нет контроля над байтовым форматом, поэтому это невозможно в сценариях взаимодействия.
  • Возможные проблемы версии (совместимость между различными версиями сборки сериализованного типа).
  • Требуется разрешение доступа к коду сериализации (актуально в сценариях частичного доверия).
3
11.12.2008 17:41:26
Мне нужен более жесткий контроль над сгенерированным пакетом, но он может быть использован в будущем.
Rex Logan 11.12.2008 17:59:59