【问题标题】:Is there a way cast an object back to it original type without specifing every case?有没有办法在不指定每种情况的情况下将对象转换回原始类型?
【发布时间】:2008-12-11 17:01:34
【问题描述】:

我有一个不同类型对象的数组,我使用 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);

但除非我每次施法,否则它不起作用。

编辑:

在咨询了答案后,这就是我想出的功能。为了测试这个函数,将数组发送到 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             
  }

【问题讨论】:

    标签: c# casting syslog udpclient


    【解决方案1】:

    没有。强制转换必须在编译时知道,但实际类型只有在执行时才知道。

    但是请注意,有一种更好的方法可以测试调用 GetType 的类型。而不是:

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

    用途:

    if (x is byte)
    

    编辑:回答额外的问题:

    “所有类型是什么?”好吧,我想看看 BinaryWriter 的文档...

    “我需要担心字节和字节吗?”不,byte 是 C# 中 System.Byte 的别名。它们是同一类型。

    【讨论】:

    • +1 表示“查看 BinaryWriter 的文档”...注意数组有重载 -- Write(char[]) 和 Write(byte[])
    • 为什么使用 Is 比 GetType() 来进行类型比较?
    【解决方案2】:

    这是一个使用反射的 BinaryWriter 解决方案。

    这基本上扫描 BinaryWriter 以查找名为 Write 的方法,该方法只接受一个参数,然后构建一个字典,其中哪个方法处理哪种类型,然后为每个要写入的对象找到正确的方法并在 writer 上调用它。

    很脏,您可能应该寻找更好的方法来完成整个事情(不仅仅是写作部分),但它应该可以满足您当前的需求:

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

    【讨论】:

    • 我曾经遇到过同样的问题,第一次尝试就按照你的方法做了,但是太慢了。将其更改为 if-else 阶梯实际上会快得多!
    【解决方案3】:

    Jon 是对的,但我有另一个想法,你可能会觉得有用。您是否考虑过在每个对象的传输中添加另一个字节,然后将该字节用作类型代码,告诉您在另一端将其转换为什么?

    【讨论】:

      【解决方案4】:

      您是否考虑过使用 BinaryFormatter 代替 BinaryWriter?

      优势

      • 您可以传递 objects(即任何东西),因此它可以解决您的投射问题。
      • 自动类型管理(实际上将类型标头写入流)。
      • 也支持复杂的引用类型。

      缺点

      在内部使用序列化,因此:

      • 可能更慢。
      • 字节流变大(由于类型标头)。
      • 您无法控制字节格式,因此在互操作方案中不是一个选项。
      • 潜在的版本问题(序列化类型的不同程序集版本之间的兼容性)。
      • 需要序列化代码访问权限(与部分信任场景相关)。

      【讨论】:

      • 我需要对生成的数据包进行更严格的控制,但将来可能会用到。
      【解决方案5】:

      您要的是Dynamic Dispatch,而C# 3.0 没有。

      您至少应该使用运行时检查来验证您没有丢失类型。

      如果你有一个从类型映射到处理函数的Dictionary,你也许可以做一些聪明的事情。您可以在一处填写所有处理功能的映射。与在处理发生的任何地方编写开关相比,您更有可能做到这一点。

      【讨论】:

      • 等不及 C#4.0。您或许应该向他展示如何在 C#4.0 中使用 dynamic 关键字来完成此操作?
      • 不知道C#4.0能不能做到。其实我怀疑OP需要的是double dynamic dispatch,而C#4.0只会增加single dynamic dispatch。但我不确定。
      【解决方案6】:

      这是一个需要名为 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)进行写入,但这是针对该对象完成的,无法提前知道。

      【讨论】:

        猜你喜欢
        • 2022-01-14
        • 2016-07-07
        • 1970-01-01
        • 1970-01-01
        • 2022-11-23
        • 2021-01-09
        • 2018-02-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多