виртуальные методы в C#
От: Arsen.Shnurkov  
Дата: 22.02.17 10:12
Оценка:
У меня от этого мозг ломается. В моей картине мира виртуальной функция Test должна быть только в классе M.

using System;

class program
{
    static void Main(string[] args)
    {
        int sum = 0;
        sum += new Base1().Test();
        sum += new Base2().Test();
        Console.WriteLine(sum);
    }
}

class Base1
{
    public int Test()
    {
        var m = typeof(Base1).GetMethod(nameof(Test));
        return m.IsVirtual ? 1 : 0;
    }
}

class Base2
{
    public int Test()
    {
        var m = typeof(Base2).GetMethod(nameof(Test));
        return m.IsVirtual ? 1 : 0;
    }
}

interface I { int Test(); }
class M : Base2, I { }

Что надо сделать, чтобы на экран был выведен 0 ? (Не меняя исходного кода)
Отредактировано 04.03.2017 21:59 VladD2 . Предыдущая версия . Еще …
Отредактировано 22.02.2017 13:03 Arsen.Shnurkov . Предыдущая версия .
Отредактировано 22.02.2017 10:30 Arsen.Shnurkov . Предыдущая версия .
Re: виртуальные методы в C#
От: kov_serg Россия  
Дата: 22.02.17 10:28
Оценка: +3
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>Что надо сделать, чтобы на экран был выведен 0 ?

Console.WriteLine(0);

или добавте метод Test в M
class M : Base2, I { 
  public int Test() { return 0; }
}
Re[2]: виртуальные методы в C#
От: Arsen.Shnurkov  
Дата: 22.02.17 10:30
Оценка:
AS>>Что надо сделать, чтобы на экран был выведен 0 ?
_>
_>Console.WriteLine(0);
_>

_>или добавте метод Test в M

Не меняя исходного кода.
Re[3]: виртуальные методы в C#
От: kov_serg Россия  
Дата: 22.02.17 10:52
Оценка: +1 :))
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>Не меняя исходного кода.

Не изменяя кода только так app.exe >null && echo 0

А так поменяйте условие на с m.IsVirtual на (m.IsVirtual && !m.IsFinal)
Re[4]: виртуальные методы в C#
От: Arsen.Shnurkov  
Дата: 22.02.17 11:05
Оценка:
_>А так поменяйте условие на с m.IsVirtual на (m.IsVirtual && !m.IsFinal)

Мне не надо узнавать, является ли метод overrideable.
Мне нужно, чтобы одинаково выглядящий код не менял своё поведение от того, что кто-то где-то дописал интерфейс.
Re[5]: виртуальные методы в C#
От: Sinix  
Дата: 22.02.17 11:17
Оценка:
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>Мне нужно, чтобы одинаково выглядящий код не менял своё поведение от того, что кто-то где-то дописал интерфейс.


Навесить свой [NonVirtualAttribute] и convention test на сборку.

Ну, или свой roslyn analyser, не сильно сложнее.
Re[5]: виртуальные методы в C#
От: kov_serg Россия  
Дата: 22.02.17 11:57
Оценка:
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>Мне нужно, чтобы одинаково выглядящий код не менял своё поведение от того, что кто-то где-то дописал интерфейс.

А в чем проблема, что у вас из-за этого бинарная совместимость ломается?
Re[6]: виртуальные методы в C#
От: Arsen.Shnurkov  
Дата: 22.02.17 12:21
Оценка:
_>А в чем проблема?

У меня от этого мозг ломается. В моей картине мира виртуальной функция Test должна быть только в классе M.
Отредактировано 22.02.2017 12:21 Arsen.Shnurkov . Предыдущая версия .
Re: виртуальные методы в C#
От: hardcase Пират http://nemerle.org
Дата: 22.02.17 14:28
Оценка: 78 (3) +1
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>У меня от этого мозг ломается. В моей картине мира виртуальной функция Test должна быть только в классе M.


Это видимо оптимизация компилятора. Похоже, что он просто экономит слоты в таблице виртуальных методов. Т.е. метод Base2.Test, доступный в исходниках, автоматечески помечается sealed virtual и его слот будет использоваться во всех его наследниках, неявно реализующих интерфейс I или любой другой интерфейс с методом Test нужной сигнатуры.

Явная реализация I.Test в классе M приводит к желамому 0 в выхлопе.

class M : Base2, I { int I.Test() { return 2; } }
/* иЗвиНите зА неРовнЫй поЧерК */
Отредактировано 22.02.2017 14:29 hardcase . Предыдущая версия .
Re[2]: виртуальные методы в C#
От: Arsen.Shnurkov  
Дата: 22.02.17 15:50
Оценка: 9 (1)
H>Это видимо оптимизация компилятора.

Чудесно!

1) можно ли её отключить?
(UPD: SO, MSDN не помогает, pragma тоже)
2) все ли компиляторы должны так делать (т.е. написано ли это в какой-нибудь спецификации, или это Undefined Behaviour)?
3) как эта конкретная оптимизация называется на английском языке, чтобы её искать в гугле

UPD2:

C# compiler does not get involved at this level. The Entire type model, inheritance, interface implementation etc. is actually handled by the CLR more specifically the CTS (Common Type System). .NET compilers mostly just generate IL code that represents intent which is later executed by the CLR where all Vtable handling etc. is taken care of.


Virtual stub dispatching (VSD) is the technique of using stubs for virtual method invocations instead of the traditional virtual method table.

BOTR
Отредактировано 22.02.2017 18:39 Arsen.Shnurkov . Предыдущая версия . Еще …
Отредактировано 22.02.2017 17:07 Arsen.Shnurkov . Предыдущая версия .
Отредактировано 22.02.2017 17:00 Arsen.Shnurkov . Предыдущая версия .
Отредактировано 22.02.2017 16:10 Arsen.Shnurkov . Предыдущая версия .
Отредактировано 22.02.2017 16:08 Arsen.Shnurkov . Предыдущая версия .
Отредактировано 22.02.2017 16:05 Arsen.Shnurkov . Предыдущая версия .
Re: виртуальные методы в C#
От: bazis1 Канада  
Дата: 22.02.17 17:36
Оценка:
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>У меня от этого мозг ломается. В моей картине мира виртуальной функция Test должна быть только в классе M.

По-моему, Вы мешаете кислое с длинным. Семантика virtual в .Net немного отличается от семантики в C++ (т.е. де-факто там 2 виртуальных метода, которые ведут себя, как один не-виртуальный, ибо друг с другом не связаны). Попробуйте переформулировать свой test case, чтобы там проверялось поведение методов, а не m.IsVirtual. Тогда все быстро встанет на свои места.
Re[3]: виртуальные методы в C#
От: vdimas Россия  
Дата: 18.03.17 19:05
Оценка:
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>Чудесно!

AS>1) можно ли её отключить?

Она и так отключена.
В байткоде есть разные коды операций — непосредственного вызова метода или виртуального (Call и Callvirt).
Т.е. суть в том, что виртуальный метод можно звать как обычный — не через таблицу.
Посмотри в ассемблере, как был вызван код — напрямую или через таблицу виртуальных ф-ий.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.