3

If I write this:

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

And try to implement it in an interface, VS generates this (as expected):

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

If I write this:

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

VS will implement it like this:

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

Side note: writing this:

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

will be implemented like this:

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

So, there seems to be a way to differentiate between the OutAttribute being present or not...but I can't figure out how (via Reflection). In both cases any of the methods to get custom attribute information (GetCustomAttributes(), GetCustomAttributeData(), etc.) report that the OutAttribute exists on all interface methods. This isn't a case either with code existing in the current project - if I reference an assembly with these interfaces, VS still generates the same code shown above.

So, how can I tell the difference between a parameter that is just "out" and one that had the "[Out]" attribute added to it explicitly?

JasonBock
  • 981
  • 7
  • 16
  • What are you trying to achieve? Why do you need it? Btw out keyword will emit the parameter as ref. Not by value. So, both of your code aren't same. Did you mean it or just an oversight? – Sriram Sakthivel May 07 '15 at 13:22
  • Share what you tried so far – James Lucas May 07 '15 at 13:24
  • @JamesLucas: he did share it. It's right there in the question. – Jeroen Vannevel May 07 '15 at 13:25
  • @JeroenVannevel he has shared the classes and interfaces he is reflecting against but not his reflection code. This is what I want to see. – James Lucas May 07 '15 at 13:27
  • I should note that I thought decompiler tools like ILSpy could tell the difference between the two, but it can't. In both cases it ignores the [Out] and just puts the "out" modifier on the parameter if it's an out parameter. If the parameter has [Out] on it but didn't have "out" (you can look at IsByRef on the ParameterType to tell the difference) then it will add it. So maybe there really isn't a way to differentiate. – JasonBock May 07 '15 at 13:32
  • @JamesLucas I've looked (on a ParameterInfo) at GetCustomAttributes(), GetCustomAttributeData(), the Attributes property, etc. There seems to be no difference between a parameter with [Out] and the out modifier and one with just the out. If that's the case, that's fine, just wanted to make sure I wasn't missing anything. – JasonBock May 07 '15 at 13:35
  • @JasonBock: I can't seem to find any solution either. It must be compiled into the exact same code, leaving the reverse process an impossible task. – Jeroen Vannevel May 07 '15 at 13:36
  • Btw why do you need it? What problem you're trying to solve? It sounds like [Xy Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) for me – Sriram Sakthivel May 07 '15 at 13:42

2 Answers2

1

Actually both of your code isn't same.

IOM is the proper use of output parameter. IOAM is just a fail.

For example try calling the method with value as opposed to the variable. It should fail to compile. Because out argument passed to the parameter must be a variable not a value.

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

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

This is because out parameter must be passed by reference. In your example IOAM you pass it by value.

MSIL code for IOM is

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

MSIL code for IAOM is

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

Note in IAOM.OutAttributeModifier a parameter isn't passed by reference.

I think this is an oversight in your case. If this is intentional, then you can differentiate it by checking whether the parameter is passed by ref or not. You do it by calling ParameterInfo.ParameterType.IsByRef.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • It still compiles, though. You can still add an [Out] to a parameter that is not an "out". Whether you can actually call it correctly or in an expected manner isn't really of interest of me. I'm generating code based on what is in an assembly given to me. Take a look at Read on FileStream, that has [In, Out] on a byte[], but it's not "out". There are also methods with [Out] and no "out" modifier. Again, I'm not so much concerned with the "correctness", just the existence of them and how to handle it. – JasonBock May 07 '15 at 14:19
  • @JasonBock In your example you can check it by `ParameterInfo.ParameterType.IsByRef` First one will return true and latter will return false. If both IL are same, you can't differentiate it obviously. – Sriram Sakthivel May 07 '15 at 14:28
0

You are being tripped-up by a C# specific feature, the distinction between the ref and the out qualifier on a method argument. Making the distinction was important to implement the definite assignment language feature, very useful to detect the usage of unassigned variables.

Problem is, the CLR does not have any support for that. It can only distinguish between passing an argument by value (ParameterAttributes.In) or by reference (ParameterAttributes.Out). Compare to the VB.NET ByVal vs ByRef keywords for example.

So how does the C# compiler know that a method compiled in another assembly passes an argument as out instead of ref? It can't find out from the ParameterAttributes, it doesn't encode the distinction at all. It couldn't find out if the source code for the other assembly was written in VB.NET, a language that doesn't have the same distinction.

You probably know the answer by know from what you discovered by trying to use Reflection. The C# compiler automatically emits the [Out] attribute on any argument that's declared out. And if the attribute is missing then it will interpret it as ref.

This is something you can see with a decompiler, like ildasm.exe:

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

Note the presence of [out]. If you change the interface to ref then it turns into:

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

And note how [out] is now missing. So in effect there is no distinction at all between your OutModifier() and OutAndOutAttributeModifier() implementation methods, they both are compiled with [Out] on the argument. Just as reflection told you.

If you need the distinction, and you certainly don't in this example, then you need to write the methods in another language, like VB.NET.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • This is basically what I'm finding out too. As long as I find that it's truly an "out", then I can just do "out int x" and not worry about generating the attribute. Only in the case if the attribute exists but the parameter isn't an "out" would I generate it in my code (which admittedly looks weird, but it's technically correct code as far as the C# compiler is concerned). – JasonBock May 07 '15 at 14:40
  • It is not entirely unusual, happens in pinvoke declarations on arguments of a reference type. As well as reference types that are used [in Remoting](http://stackoverflow.com/a/9073985/17034). – Hans Passant May 07 '15 at 14:45