我有一个不同类型对象的数组,我使用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
}
答案 0 :(得分:7)
没有。必须在编译时知道强制转换,但实际类型只在执行时才知道。
但请注意,有一种更好的方法可以测试调用GetType的类型。而不是:
if (x.GetType() == typeof(byte))
使用:
if (x is byte)
编辑:回答额外的问题:
“所有类型都是什么?”那么,请查看BinaryWriter的文档,我猜......
“我需要担心字节和字节吗?”不,byte是C#中System.Byte的别名。它们属于同一类型。
答案 1 :(得分:3)
乔恩是对的,但我还有另一种想法,你可能觉得有用。您是否考虑在每个对象的传输中添加另一个字节,然后将该字节用作类型代码,告诉您在另一端将其转换为什么?
答案 2 :(得分:3)
您是否考虑使用BinaryFormatter而不是BinaryWriter?
<强>优点强>
<强>缺点强>
在内部使用序列化,因此:
答案 3 :(得分:2)
您要求的是动态调度,而C#3.0没有。
您至少应该使用运行时检查来验证您是否错过了类型。
如果您拥有从类型映射到处理函数的Dictionary
,您可以做一些聪明的事情。您可以在一个地方填写所有处理功能的映射。如果在处理发生的任何地方写一个开关,你有更好的机会做到这一点。
答案 4 :(得分: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);
}
}
}
}
}
答案 5 :(得分: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正在做的是调度回输入,告诉它使用它(Writer)来编写,但这是为该对象完成的,不能提前知道。