Получают ли экземпляры struct упакованные объекты при вызове метода расширения в рекурсивном универсальном интерфейсе? - PullRequest
2 голосов
/ 31 марта 2019

У меня есть «рекурсивный универсальный интерфейс»:

public interface MyInterface<T> where T : MyInterface<T>
{
    T DoSomething();
}

И я определил для него метод расширения:

public static class MyExtensions
{
    public static T DoSomethingElse<T>(this T t)
        where T : MyInterface<T>
    {
        Console.WriteLine("DoSomethingElse was called.");
        return t.DoSomething();
    }
}

И затем я реализовал интерфейс в структуре:

public struct MyStruct : MyInterface<MyStruct>
{
    public MyStruct DoSomething()
    {
        Console.WriteLine("DoSomething was called.");
        return new MyStruct();
    }
}

И затем я вызвал метод расширения в main:

public class Program
{
    static void Main(string[] args)
    {
        MyStruct x = new MyStruct();
        MyStruct y = x.DoSomethingElse();
        Console.ReadKey();
    }
}

Вопрос: Когда вызывается DoSomethingElse, попадает ли объект MyStruct x в MyInterface, или метод расширения работает непосредственно на x?

У меня есть основания полагать, что оба:

  • x в штучной упаковке: это будет иметь место, еслиинтерфейс не был рекурсивным, так что, возможно, в данном случае метод расширения требует что-то типа MyInterface для работы, поэтому бокс
  • x не упакован: параметр T t означает структуру MyStruct напрямую, чтобы его можно было передавать без упаковки.

Я очень старался придумать способ проверить это, но не могу найти способ запечатлеть момент, когда структураприведен к типу intreface.

Прошу прощения за зверскоеназвание, но я не мог бы сказать это лучше, пожалуйста, не стесняйтесь редактировать его, если вы можете описать проблему в более простых терминах.

1 Ответ

3 голосов
/ 31 марта 2019

Без бокса.

Это можно увидеть, поместив свой код в sharplab здесь .Нет box инструкции.Существует ограниченный виртуальный вызов, который может блокировать в некоторых случаях, но не здесь.

Если вы приведете структуру к ее типу интерфейса, это будет:

MyInterface x = new MyStruct();

Однако, вызов универсального метода, подобного этому (независимо от того, ограничен ли параметр универсального типа типом интерфейса), не имеет значения.Когда вы вызываете MyExtensions.DoSomethingElse<MyStruct>, JIT генерирует новую реализацию метода, в которой специально используется MyStruct - следовательно, никакой бокс не требуется.

MyExtensions.DoSomethingElse делает ограниченный виртуальный вызов для вызова метода DoSomething.Это используется главным образом, когда компилятор не уверен, будет ли цель значением или ссылочным типом во время выполнения, и он в основном говорит: «JIT, вы выясните это».В частности:

  • Если thisType является типом значения и thisType реализует method, тогда ptr передается неизмененным как указатель 'this' на call method инструкция по реализации method по thisType.
  • Если thisType является типом значения и thisType не реализует method, тогда ptr разыменовывается, упаковывается и передается как указатель 'this' на инструкцию callvirt method,

Здесь мы попали в первый случай, и поэтому никакого бокса не происходит.Если бы вы позвонили t.GetHashCode() или t.GetType(), то JIT отправил бы инструкции на ящик t.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...