在反射中区分OutAttribute和out修饰符

时间:2015-05-07 13:14:23

标签: c# reflection

如果我写这个:

public interface IOutModifier
{
    void OutModifier(out int a);
}

尝试在接口中实现它,VS生成它(如预期的那样):

public class IOM : IOutModifier
{
    public void OutModifier(out int a)
    {
        throw new NotImplementedException();
    }
}

如果我写这个:

public interface IOutAndOutAttributeModifier
{
    void OutAndOutAttributeModifier([Out] out int a);
}

VS会像这样实现它:

public class IOOAM : IOutAndOutAttributeModifier
{
    public void OutAndOutAttributeModifier([Out]out int a)
    {
        throw new NotImplementedException();
    }
}

旁注:写下这个:

public interface IOutAttributeModifier
{
    void OutAttributeModifier([Out] int a);
}

将实现如下:

public class IOAM : IOutAttributeModifier
{
    public void OutAttributeModifier([Out] int a)
    {
        throw new NotImplementedException();
    }
}

所以,似乎有办法区分OutAttribute是否存在......但我无法弄清楚如何(通过Reflection)。在这两种情况下,任何获取自定义属性信息的方法(GetCustomAttributes(),GetCustomAttributeData()等)都会报告OutAttribute存在于所有接口方法中。这不是当前项目中存在代码的情况 - 如果我使用这些接口引用程序集,VS仍会生成上面显示的相同代码。

那么,我如何区分一个只是“out”的参数和一个明确添加了“[Out]”属性的参数呢?

2 个答案:

答案 0 :(得分:1)

实际上你的两个代码都不一样。

IOM正确使用输出参数。 IOAM只是失败。

例如,尝试使用值而不是变量调用方法。它应该无法编译。因为传递给参数的out参数必须是变量而不是值。

IOM iom = new IOM();
iom.OutModifier(4);//Fails as expected

IOAM ioam = new IOAM();
ioam.OutAttributeModifier(4);//Compiles fine

这是因为out参数必须通过引用传递。在您的示例IOAM中,您可以按值传递它。

IOM的MSIL代码是

.method public hidebysig newslot virtual final instance void OutModifier([out] int32& a) cil managed

IAOM的MSIL代码是

.method public hidebysig newslot virtual final instance void OutAttributeModifier([out] int32 a) cil managed

注意IAOM.OutAttributeModifier a参数不通过引用传递。

我认为这是对你案件的疏忽。如果这是故意的,那么你可以通过检查ref是否传递参数来区分它。您可以致电ParameterInfo.ParameterType.IsByRef

答案 1 :(得分:0)

您正被一个C#特定功能绊倒,即方法参数上refout限定符之间的区别。区分对于实现definite assignment语言功能非常重要,对于检测未分配变量的使用非常有用。

问题是,CLR对此没有任何支持。它只能区分通过值(ParameterAttributes.In)或引用(ParameterAttributes.Out)传递参数。例如,与VB.NET ByVal vs ByRef关键字进行比较。

那么C#编译器如何知道在另一个程序集中编译的方法将参数传递为out而不是ref?它无法从ParameterAttributes中找到它根本不编码的区别。它无法确定其他程序集的源代码是否是用VB.NET编写的,这种语言没有相同的区别。

您可能通过尝试使用Reflection发现的内容知道答案。 C#编译器自动会在声明为[Out]的任何参数上发出out属性。如果该属性丢失,则会将其解释为ref

这是你可以用反编译器看到的东西,比如ild​​asm.exe:

.method public hidebysig newslot virtual final 
        instance void  OutModifier([out] int32& a) cil managed
// etc...

注意[out]的存在。如果您将界面更改为ref,则会变为:

.method public hidebysig newslot virtual final 
        instance void  OutModifier(int32& a) cil managed

并注意[out]现在如何丢失。因此实际上,在OutModifier()和OutAndOutAttributeModifier()实现方法之间没有任何区别,它们都在参数上使用[Out]进行编译。正如反思告诉你的那样。

如果你需要这个区别,你肯定不在这个例子中,那么你需要用另一种语言编写方法,比如VB.NET。