C# 反射中OutAttribute和out修饰符的区分

C# 反射中OutAttribute和out修饰符的区分,c#,reflection,C#,Reflection,如果我这样写: public interface IOutModifier { void OutModifier(out int a); } public interface IOutAndOutAttributeModifier { void OutAndOutAttributeModifier([Out] out int a); } 并尝试在接口中实现它,VS会生成以下内容(如预期的那样): 如果我这样写: public interface IOutModifier {

如果我这样写:

public interface IOutModifier
{
    void OutModifier(out int a);
}
public interface IOutAndOutAttributeModifier
{
    void OutAndOutAttributeModifier([Out] out int a);
}
并尝试在接口中实现它,VS会生成以下内容(如预期的那样):

如果我这样写:

public interface IOutModifier
{
    void OutModifier(out int a);
}
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 class IOAM : IOutAttributeModifier
{
    public void OutAttributeModifier([Out] int a)
    {
        throw new NotImplementedException();
    }
}
旁注:写这个:

public interface IOutAttributeModifier
{
    void OutAttributeModifier([Out] int a);
}
将按以下方式实施:

public class IOOAM : IOutAndOutAttributeModifier
{
    public void OutAndOutAttributeModifier([Out]out int a)
    {
        throw new NotImplementedException();
    }
}
public class IOAM : IOutAttributeModifier
{
    public void OutAttributeModifier([Out] int a)
    {
        throw new NotImplementedException();
    }
}
所以,似乎有一种方法可以区分OutAttribute是否存在……但我不知道如何(通过反射)。在这两种情况下,用于获取自定义属性信息的任何方法(GetCustomAttributes()、GetCustomAttributeData()等)都报告OutAttribute存在于所有接口方法上。当前项目中存在的代码也不是这种情况——如果我引用具有这些接口的程序集,VS仍然会生成与上面所示相同的代码


那么,我如何区分只是“out”的参数和显式添加了“[out]”属性的参数之间的区别呢?

实际上,您的两个代码并不相同

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
中,
参数不是通过引用传递的


我认为这是你的疏忽。如果这是有意的,那么可以通过检查参数是否通过ref传递来区分它。通过调用
ParameterInfo.ParameterType.IsByRef

可以做到这一点,因为方法参数上的
ref
out
限定符之间的区别是C#特有的特性。进行区分对于实现语言功能非常重要,对于检测未分配变量的使用非常有用

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

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

你可能知道答案,从你尝试使用反射的发现中知道答案。C#编译器自动对声明为
Out
的任何参数发出
[Out]
属性。如果属性丢失,则它将解释为
ref

这是您可以通过反编译器看到的内容,如ildasm.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。

您想实现什么?你为什么需要它?Btw out关键字将以ref而非value的形式发出参数。所以,您的两个代码都不相同。你是认真的还是只是一个疏忽?分享你的努力far@JamesLucas当前位置他确实分享了它。问题就在这里。@Jeroenvanevel他已经分享了他正在反思的类和接口,但没有分享他的反思代码。这就是我想看到的。我应该注意到,我认为像ILSpy这样的反编译工具可以区分两者,但它不能。在这两种情况下,它都会忽略[Out],如果是Out参数,则只在参数上添加“Out”修饰符。如果参数上有[Out],但没有“Out”(您可以查看参数类型上的IsByRef来判断差异),那么它将添加它。因此,也许真的没有一种方法来区分。尽管如此,它仍然可以编译。您仍然可以将[Out]添加到不是“Out”的参数中。我对你是否能正确地或以预期的方式称呼它并不感兴趣。我正在根据给定给我的程序集中的内容生成代码。看一看FileStream上的Read,它在一个字节[]上有[In,Out],但不是“Out”。也有带有[Out]且没有“Out”修饰符的方法。同样,我并不太关心“正确性”,只关心它们的存在以及如何处理它。@JasonBock在您的示例中,您可以通过
ParameterInfo.ParameterType.IsByRef来检查它,第一个将返回true,第二个将返回false。如果两个IL都是相同的,你就不能很明显地区分它。这基本上也是我发现的。只要我发现它确实是一个“out”,那么我就可以执行“out int x”,而不用担心生成属性。只有在属性存在但参数不是“out”的情况下,我才会在代码中生成它(这看起来很奇怪,但就C#编译器而言,它在技术上是正确的代码)。这并非完全不寻常,发生在引用类型参数的pinvoke声明中。以及所使用的引用类型。