2008-12-11 5 views
1

У меня есть массив объектов другого типа, и я использую 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); 

но это не сработает, если я не делаю каждого броска.

Edit:

После консультаций ответов это то, что я придумал для этой функции. Для тестирования эта функция отправляет массив в syslog.

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    
    } 

ответ

2

Это решение для 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); 
       } 
      } 
     } 
    } 
} 
7

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

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

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

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

if (x is byte) 

EDIT: Для того, чтобы ответить на дополнительные вопросы:

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

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

+0

+1 за «смотреть вниз документы для BinaryWriter "... обратите внимание на наличие перегрузок для массивов - Write (char []) и Write (byte []) – Jimmy 2008-12-11 17:45:09

+0

Почему лучше использовать GetType() для сравнения типов? – sthay 2008-12-21 04:23:25

+1

Это быстрее, код проще, и он недействителен. – 2008-12-21 07:59:48

3

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

2

То, о чем вы просите, это Динамическая отправка, а C# 3.0 - нет.

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

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

+0

Не могу подождать C# 4.0. Возможно, вы должны показать ему пример того, как это можно сделать в C# 4.0 с помощью динамического ключевого слова? – Llyle 2008-12-11 17:20:31

1

Это случай, когда требуется что-то, что называется 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) писать, однако, что это делается для того объекта, который не может быть известна заранее времени.

3

Считаете ли вы использование BinaryFormatter вместо BinaryWriter?

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

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

Недостатки

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

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