代码之家  ›  专栏  ›  技术社区  ›  Rex Logan

是否有一种方法可以将对象强制转换回其原始类型而不指定每种情况?

  •  1
  • Rex Logan  · 技术社区  · 17 年前

    我有一个不同类型对象的数组,我使用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             
      }
    
    6 回复  |  直到 17 年前
        1
  •  2
  •   Lasse V. Karlsen    17 年前

    下面是使用反射的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);
                    }
                }
            }
        }
    }
    
        2
  •  7
  •   Jon Skeet    17 年前

    不是。强制转换必须在编译时已知,但实际类型仅在执行时已知。

    但是请注意,有更好的方法来测试调用gettype的类型。而不是:

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

    用途:

    if (x is byte)
    

    编辑:要回答额外的问题:

    “所有类型都是什么?”好吧,我想查一下二元书写器的文档吧…

    “我需要担心字节和字节吗?”不,byte是system.byte的别名,用c_表示。他们是同一类型的。

        3
  •  3
  •   Ian Jacobs    17 年前

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

        4
  •  3
  •   Christoph Rüegg    17 年前

    你考虑过使用 BinaryFormatter 而不是二元编写器?

    优势

    • 你可以通过 对象 所以它解决了你的铸造问题。
    • 自动类型管理(实际上将类型头写入流)。
    • 也支持复杂的引用类型。

    缺点

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

    • 可能更慢。
    • 字节流变大(因为类型头)。
    • 您不能控制字节格式,因此不能在互操作方案中使用选项。
    • 潜在的版本问题(序列化类型的不同程序集版本之间的兼容性)。
    • 需要序列化代码访问权限(与部分信任方案相关)。
        5
  •  2
  •   Jay Bazuzi Buck Hodges    17 年前

    你要的是 动态调度 C 3.0没有。

    您至少应该使用运行时检查来验证是否缺少类型。

    如果你有一个 Dictionary 它从类型映射到处理函数。您可以在一个地方填写所有处理函数的映射。比起在任何处理发生的地方编写一个开关,您有更好的机会得到正确的处理。

        6
  •  1
  •   moffdub    17 年前

    这是一个需要叫做 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);
        }
    }
    

    编写器正在做的是将数据分派回输入端,告诉它使用它(编写器)来编写数据,但是这是为那个对象完成的,而这个对象不能提前知道。