在.net结构构造函数中使用try-catch

时间:2012-04-19 16:15:00

标签: c# .net try-catch structure

我需要一个.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( );
       }
    }
}

8 个答案:

答案 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和对齐差异。