【问题标题】:'out' parameter versus generic/template argument: best practice'out' 参数与泛型/模板参数:最佳实践
【发布时间】:2012-09-05 04:58:14
【问题描述】:

我正在做一些数据解析并遇到了这个问题。假设我们想将一些 byte[] 解析为一个结构。我想将执行此操作的 C# 代码包装到静态方法中。

原始代码(我正在修改一段)阅读:

public class DiagnosticUndefined : BaseDiagnostic
{
    StructDiagnosticUndefined bufferAllocation;

    public DiagnosticUndefined(byte[] buff)
    {
        bufferAllocation = (StructDiagnosticUndefined)DiagnosticUtil.parseStruct(buff, typeof(StructDiagnosticUndefined));
    }
}

我想为此使用一个通用函数,但是如何进行呢?考虑:

public static class Util {
    public static T Convert<T>(byte[] data) {...}
    public static void Convert<T>(byte[] data, out T structure) {...}
}

第一个更符合正常过程,但缺点是编译器无法推断数据类型,所以我的调用将如下所示:

SomeStruct s;
s = Util.Convert<SomeStruct>(data);

另一种方法是这样的:

SomeStruct s;
Util.Convert(data, out s);

我喜欢第二种方法,因为它将类型推断委托给编译器,即运行时错误更少。另一方面,我倾向于避免使用 MSDN 支持的 out 参数:http://msdn.microsoft.com/en-us/library/ms182131.aspx。我完全赞成“不要以复杂的方式解决简单问题”的范式,但这次我无法区分......

有什么提示、意见吗?

更新

代码示例被简化,变量实际上是一个成员,所以我不能“单行”。我也在使用 Marshalling 将数据转换为结构:

GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
T output = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();

【问题讨论】:

  • 第一个看起来应该不会比var s = Util.Convert&lt;SomeStruct&gt;(data);更复杂
  • 您说“我喜欢第二种方法,因为它将类型推断委托给编译器,即运行时错误更少”。这句话在这种情况下很奇怪,因为在这两种方法中都不可能因为类型而出现运行时错误。在这两种情况下,编译器都会检查类型是否匹配。
  • @DanielHilgarth 函数版本允许隐式转换,如decimal d = Util.Convert&lt;int&gt;(data);
  • @hvd:好的 - 但这仍然不会导致运行时错误。
  • 如果data.Lengthint 的预期长度不匹配,@DanielHilgarth Util.Convert&lt;int&gt; 可能会引发异常,或者可能返回与Util.Convert&lt;decimal&gt; 返回的数据完全不同的数据。错误的数据也是运行时错误,只是不是运行时异常。

标签: c# generics out


【解决方案1】:

@Nebula 建议编辑

第一种情况,似乎完全有效:

var s = Util.Convert<SomeStruct>(data);

当您想从通话中返回某些东西时,请使用out,但用于声明性目的。

【讨论】:

  • 我猜,OP 想在第二个例子中使用它:var s = Util.Convert(data); 没有明确编码 T
  • @KonstantinVasilcov:老实说,没有看到不使用泛型声明、假装类型安全和使用 out 参数的原因
  • @Tigran 康斯坦丁说的。我不完全理解你的评论。您是支持还是反对明确输入他的泛型?
  • @Nebula:是的,为了明确声明你的函数在 out 上运行的类型,你不需要使用,只需使用 generics。使用 out 以防您想从通话中返回某些内容,但不用于声明性目的。
  • @Nebula:我会选择你的第一个案例。因为它做你想做的事(考虑到提供的问题)。
【解决方案2】:

两种方法,都不需要创建SomeStruct对象:

 SomeStruct s = new SomeStruct();

因为我相信你在Convert 方法中创建了这个对象。对于第二种方法,正确性应该是:

SomeStruct s;
Util.Convert(data, out s);

因为out 参数不需要初始化。如果您只是更改s 的属性而不更改指针或在Convert 内创建对象,则也不需要out

SomeStruct s = new SomeStruct();
Util.Convert(data, s);

恕我直言,方法 1 应该更好,更具可读性。

【讨论】:

  • +1: 对于在某处之前构造 SomeStruct 并删除 out 修饰符 => 的想法,OP 具有所需的类型推断并避免 out
  • 我实际上并没有事先创建对象。我已经相应地更新了我的答案。感谢您澄清其他可用选项。
  • @KonstantinVasilcov 我不希望类型推断,我实际上想知道使用它是否是一种好习惯。所以请给我你的意见,这样我就可以形成我的:-)
【解决方案3】:

我很确定在这种情况下使用泛型不会给您带来太多优势。但如果你坚持... 怎么了

var s = Util.Convert<SomeStruct>(d);

另外,转换和解析不是一回事,不要互换使用。

【讨论】:

  • 是的,我知道。我的生产代码有一个不同的名称,更适合任务。上面的代码是一个例子。泛型给我带来的好处是,我无需编写返回 object 的函数(我必须强制转换),而是可以使用 C# 语法定义类型,即更具可读性。
  • 顺便说一下,请详细说明“我很确定在这种情况下使用泛型不会给您带来太多优势”部分?这种方式并不真正具有建设性。如果您有更好的方法,那么我很乐意听到。毕竟这是一个最佳实践问题。
  • 好吧,我不知道您正在使用 Marshal 进行“转换”。这对于 C# 代码来说是非常不正统的。因此,如果您坚持使用它,请忽略我的此评论。否则,读取二进制数据并生成对象的方法很可能称为 Deserialize 并且是特定于类型的:SomeStruct DeserializeSomeStruct(byte[] input);
【解决方案4】:

我会将第一个修改为:

SomeStruct s = Util.Convert<SomeStruct>(data); 

然后继续。

原因是阅读和维护的代码更少。

【讨论】:

  • 成员变量并不妨碍你写_s = Util.Convert&lt;SomeStruct&gt;(data);
  • @DanielHilgarth 不,但这就是正确的吗?但是,更少代码的论点就消失了,因为两种解决方案都读取一行。较少维护的论点:泛型具有类型相关的参数,而“out”参数没有。当我更改输出的类型时,我必须在“out”解决方案中更改泛型解决方案中的两行(var 类型和 gen arg 类型),我只需要更改变量的类型,其余的就会自动跟随。但是,我又回到了我的问题; 'out':更少依赖类型的代码,泛型:在大多数情况下被认为更“正确”。这是一个例外吗?
  • @Nebula,我真的不认为 C# 允许您将私有类成员作为 out 参数传递给外部方法。这有效地使类外部的代码能够更新内部类数据。如果内存服务正确,C++ 不允许这样做,我认为这更好,更 OO。所以,我知道 C# 允许它,但我仍然会选择返回参数,作为我个人的偏好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 2011-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多