Подтвердить что ты не робот

С# ошибка компилятора или обычная COM-странность?

С# 4, чтобы упростить COM-взаимодействие, разрешить вызывающим абонентам COM-интерфейсам опускать ключевое слово ref перед аргументами по параметрам ref.

Я был удивлен, увидев сегодня, что это также относится к методам расширения, которые расширяют интерфейсы COM. См. Следующее, компиляция, код:

using System;
using System.Runtime.InteropServices;

[ComImport, Guid ("cb4ac859-0589-483e-934d-b27845d5fe74")]
interface IFoo {
}

static class Program {

    public static void Bar (this IFoo self, ref Guid id)
    {
        id = Guid.NewGuid ();
    }

    static void Main ()
    {
        Foo (null);
    }

    static void Foo (IFoo o)
    {
        Guid g = Guid.NewGuid ();
        Console.WriteLine (g);

        // note that g is passed as is, and not as ref g    
        o.Bar (g);

        Console.WriteLine (g);
    }
}

Я не нашел ничего в спецификации, чтобы объяснить это поведение.

Мое чувство было бы в том, что код вне COM-интерфейса, даже если он использует метод расширения, расширяющий COM-интерфейс, должен следовать обычным правилам С# и обеспечивать использование ключевого слова ref. Поэтому я подал сообщение об ошибке при подключении. Не то чтобы я думаю, что это будет исправлено, даже если оно будет считаться ошибкой, там уже есть код, полагающийся на это.

Bug? Не ошибка?

4b9b3361

Ответ 1

Я не думаю, что это ошибка; он больше похож на "COM voodoo", как вы говорите. Под капотом компилятор С# испускает то, что на самом деле правильно, например:

private static void Foo(IFoo o)
{
    ...
    Guid g = Guid.NewGuid();
    Guid <>r__ComRefCallLocal0 = g;
    Bar(o, ref <>r__ComRefCallLocal0);
    ...
}

С# на самом деле полна трюков. Если вы добавите метод в IFoo, например,

[ComImport, Guid("cb4ac859-0589-483e-934d-b27845d5fe74")]
interface IFoo
{
    void Test([Optional] ref object test);
}

вы, опять же, сможете объявить это в С# 4:

static void Foo(IFoo o)
{
    Guid g = Guid.NewGuid();
    o.Test(g);
}

Конечно, все это работает только потому, что CSC.EXE обладает глубоким знанием атрибута ComImport. Эти новые магические трюки Interop были добавлены в С# 4.0, чтобы иметь возможность легко взаимодействовать с существующими COM-интерфейсами. Ну, для интерфейсов и методов Microsoft Office в основном, и особенно для армий ужасных параметров "ref missing": -)

Я не думаю, что это полностью указано где угодно. Это все, что должно сказать спецификация С# 4:

17.5 Атрибуты для взаимодействия. Примечание. Этот раздел применим только к реализации Microsoft.NET С#. 17.5.1. Взаимодействие с компонентами COM и Win32. Время выполнения .NET предоставляет большое количество атрибутов, которые позволяют программам С# взаимодействовать с компонентами, написанными с использованием COM и Win32 DLL. Для Например, атрибут DllImport можно использовать для статического метода extern чтобы указать, что реализация метода находится в Win32 DLL. Эти атрибуты находятся в Пространство имен System.Runtime.InteropServices и подробная документация для этих атрибутов содержится в документации по времени выполнения .NET.

И вот несколько страниц на MSDN: