我需要一个.net结构(它模仿连接的设备内部地图),我想使用try catch块,因为我使用Marshall.PtrToStructure()和相关的GChandle东西。但是,当我在try catch块中放置结构字段赋值时,我得到此错误“必须在控制权返回给发件人之前完全分配field1”。没有try catch块,基本代码工作正常。使用try catch块时是否有任何解决此错误的方法?我应该使用try catch吗?
[StructLayout( LayoutKind.Sequential )]
public struct Effects
{
public UInt16 field_1;
public UInt16 field_2;
...
public Effects(byte[] effectsData)
{
GCHandle gch;
try
{
gch = GCHandle.Alloc( effectsData, GCHandleType.Pinned );
IntPtr pEffects = gch.AddrOfPinnedObject( );
this = (Effects)Marshal.PtrToStructure( pEffects, typeof(Effects ) );
}
catch (Exception ex)
{
}
finally
{
if (gch.IsAllocated)
gch.Free( );
}
}
}
答案 0 :(得分:13)
构造函数必须保证如果正常返回,则所有字段都填入。
假设您的代码在try
的第一行引发异常。然后你抓住异常,吃掉它,然后返回,而没有填满任何领域!编译器检测到此并禁止该程序。
在这里吃每一个例外几乎肯定都是错误的。如果存在未处理且意外的任意异常,您真的想要返回未初始化的结构吗?
如果那是你想要做的,那么你可以简单地说:
public Effects(byte[] effectsData) : this() {
将保证在ctor块运行之前将字段初始化为其默认值。
但是又一次:那真的是你想要做的吗?这段代码看起来非常危险。
答案 1 :(得分:4)
如果发生异常,您希望发生什么?正如您的代码现在所处,如果try块中发生异常,则吞下异常(因为您的catch块为空),并且结构未初始化 - 这正是编译器所抱怨的。
如果只需要最终部分的try-catch块,请不要使用catch块,只需使用try-finally。通过不捕获错误,让错误冒泡到用户界面。没有捕获块(几乎)总是比空的捕获块更好。空的catch块使调试成为一场噩梦,因为你的代码只是“不起作用”而没有指出出错的地方。
因此,我会按如下方式重写您的代码:
public Effects(byte[] effectsData)
{
GCHandle gch = GCHandle.Alloc( effectsData, GCHandleType.Pinned );
try
{
IntPtr pEffects = gch.AddrOfPinnedObject( );
this = (Effects)Marshal.PtrToStructure( pEffects, typeof(Effects ) );
}
finally
{
if (gch.IsAllocated)
gch.Free( );
}
}
(如果在GCHandle.Alloc
中发生错误,则不会分配gch,因此不需要将其包含在try-finally块中。)
答案 2 :(得分:2)
将其添加到构造函数
field_1 = 0;
field_2 = 0;
...
结构的行为与类不同。您必须在构造函数中明确指定所有字段。
答案 3 :(得分:2)
您必须在构造函数中设置结构的所有属性。
在插入try / catch时,并非所有代码路径都允许设置这些属性。
您可能需要以下内容:
try
{
// Tries to affect something
// Then returns
return;
}
catch (Exception ex)
{
// Set default values
this.field1 = ....
}
finally
{
if (gch.IsAllocated)
gch.Free( );
}
答案 4 :(得分:1)
在离开构造函数之前,必须初始化struct的成员。
在进入try-catch循环之前,在捕获异常时抛出某些东西或者将数据成员初始化为合理的东西。或者您可以将值初始化为catch块内的某些内容。
答案 5 :(得分:1)
问题是空的catch块:如果发生异常,应该如何初始化结构?
如果您真的想忽略异常,则必须将this
初始化为catch块中的默认值。要将所有字段设置为零,只需使用
this = new Effects();
或者您可以通过让异常传播出构造函数来避免此问题。
答案 6 :(得分:1)
与提供第二个构造函数相比,静态成员的问题和混淆程度要小得多:
public static Effects Create(byte[] effectsData)
{
GCHandle gch;
try
{
gch = GCHandle.Alloc( effectsData, GCHandleType.Pinned );
IntPtr pEffects = gch.AddrOfPinnedObject( );
return (Effects)Marshal.PtrToStructure( pEffects, typeof(Effects) );
}
finally
{
if (gch.IsAllocated)
gch.Free( );
}
}
在这种情况下,异常处理很容易理解和维护。
答案 7 :(得分:0)
当然你必须知道字节数组的布局吗?使用BitConverter
类初始化字段。这样您就不必担心固定,尝试最终和其他不必要的开销。
[StructLayout( LayoutKind.Sequential )]
public struct Effects
{
public UInt16 field_1;
public UInt16 field_2;
public static Effects FromBytes(byte[] data)
{
var value = new Effects();
value.field_1 = BitConverter.ToUInt16(data, 0);
value.field_2 = BitConverter.ToUInt16(data, 2);
return value;
}
}
进行必要的调整以适应endianness和对齐差异。