4

我需要一个 .net 结构(它模仿连接的设备内部映射)并且我想使用 try catch 块,因为我正在使用 Marshall.PtrToStructure() 和相关的 GChandle 东西。但是,当我将结构字段分配放在 try catch 块中时,我收到此错误“在将控制权返回给发送者之前必须完全分配字段 1”。没有 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( );
       }
    }
}
4

8 回答 8

13

构造函数必须保证如果它正常返回则所有字段都被填写

假设您的代码在try. 然后你捕捉到异常,吃掉它,然后返回,而无需填写任何字段!编译器检测到这一点并禁止该程序。

吃掉每一个例外几乎肯定是在这里做的错误的事情。如果存在未处理和意外的任意异常,您真的要返回未初始化的结构吗?

如果那是您想要做的,那么您可以简单地说:

public Effects(byte[] effectsData) : this() {

这将保证在 ctor 块运行之前将字段初始化为其默认值。

但再说一遍:这真的是你想要做的吗?这段代码对我来说看起来非常危险。

于 2012-04-19T16:21:46.753 回答
4

如果发生异常,您希望发生什么?就您现在的代码而言,如果 try 块中发生异常,则会吞下异常(因为您的 catch 块是空的),并且结构未初始化——这正是编译器所抱怨的。

如果您只需要在 finally 部分使用 try-catch 块,则根本不要使用 catch 块,而只使用 try-finally。通过不捕获错误,让错误冒泡到用户界面。没有 catch 块(几乎)总是比空的 catch 块好。空的 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 块中。)

于 2012-04-19T16:20:46.970 回答
2

将此添加到构造函数

field_1 = 0;
field_2 = 0;
...

结构的行为与类不同。您必须在构造函数中显式分配所有字段。

于 2012-04-19T16:20:12.690 回答
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( );
}
于 2012-04-19T16:20:38.313 回答
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( );
   }
}

这种情况下的异常处理很容易理解和维护。

于 2012-04-19T16:27:17.417 回答
1

在离开构造函数之前,必须初始化结构的成员。

在捕获异常时抛出一些东西,或者在进入 try-catch 循环之前将数据成员初始化为合理的东西。或者您可以将这些值初始化为 catch 块内的某些内容。

于 2012-04-19T16:18:26.627 回答
1

问题是空的catch块:如果发生异常应该如何初始化结构?

如果您真的想忽略异常,则必须this在 catch 块中初始化为默认值。要将所有字段设置为零,只需使用

this = new Effects();

或者,您可以通过让异常传播到构造函数之外来避免该问题。

于 2012-04-19T16:19:49.320 回答
0

你肯定知道字节数组的布局吧?使用BitConverter类来初始化字段。这样您就不必担心 pinning、try-finally 和其他不必要的开销。

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

进行任何必要的调整以适应字节顺序和对齐差异。

于 2012-04-19T17:31:44.490 回答