Re[2]: [Этюд] Сломанный деструктор
От: Lloyd Россия  
Дата: 21.09.10 09:02
Оценка: :))) :))
Здравствуйте, Sinix, Вы писали:

S>А вот так:

S>
S>    class DontSayGoodbye
S>    {
S>      ~DontSayGoodbye()
S>      {
S>        Console.Clear();
S>        Console.WriteLine("hello");
S>      }
S>    }
S>

S>?

Да ты шалун.
Re[2]: эээ?
От: Константин Л.  
Дата: 21.09.10 10:37
Оценка:
Здравствуйте, Aznog, Вы писали:

>>> ...

>>> Как устроен DontSayGoodbye?
A>
A>    abstract class DontSayGoodbye
A>    {
A>        protected abstract void Finalize();
A>    }
A>


из-за protected? каков механизм?
Re[3]: эээ?
От: Пельмешко Россия blog
Дата: 21.09.10 10:58
Оценка: 99 (6)
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, Aznog, Вы писали:


>>>> ...

>>>> Как устроен DontSayGoodbye?
A>>
A>>    abstract class DontSayGoodbye
A>>    {
A>>        protected abstract void Finalize();
A>>    }
A>>


КЛ>из-за protected? каков механизм?


Дело в том, что компилятор C# при компилировании деструктора должен сгенерировать такой вот код:

protected override void Finalize()
{
    try
    {
        Console.WriteLine("goodbye");
    }
    finally
    {
        base.Finalize();
    }
}

То есть деструктор должен вызвать предудыщие переопределения метода object.Finalize() после своего тела.
Так вот, разработчики компилятора C# при генерации данного кода видимо тупо искали а базовых классах виртуальный метод по имени "Finalize" и генерировали для него base-вызов (который в C# всегда невиртуальный), так что если создать абстрактный метод (или виртуальный, но не override!) с таким именем, то компилятор C# успешно генерирует невиртуальный вызов абстрактного метода:

.method family hidebysig virtual instance void Finalize() cil managed
{
    .maxstack 1
    L_0000: nop 
    L_0001: ldstr "goodbye"
    L_0006: call void [mscorlib]System.Console::WriteLine(string)
    L_000b: nop 
    L_000c: nop 
    L_000d: leave.s L_0017
    L_000f: ldarg.0 
    L_0010: call instance void DontSayGoodbye::Finalize()
    L_0015: nop 
    L_0016: endfinally 
    L_0017: nop 
    L_0018: ret 
    .try L_0000 to L_000f finally handler L_000f to L_0017
}

Такой il код не проходит верификацию peverify,

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: [ConsoleApplication1.exe : Goodbye::Finalize][offset 0x00000010] Call not allowed on abstract methods.
1 Error(s) Verifying bin\Debug\ConsoleApplication1.exe

А в рантайме вызов финализера, видимо, просто молча валится на этапе JIT'а...

Как-то так
Re[4]: эээ?
От: Константин Л.  
Дата: 21.09.10 11:01
Оценка:
Здравствуйте, Пельмешко, Вы писали:

[]

круто
Re[2]: [Этюд] Сломанный деструктор
От: Пельмешко Россия blog
Дата: 21.09.10 11:05
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, Mab, Вы писали:


Mab>>Как устроен DontSayGoodbye?


_FR>И мои пять копеек:


А вот так точно никогда не вызывает финализер

using System;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

[DontSayGoodbye]
class DontSayGoodbye : ContextBoundObject
{
    sealed class DontSayGoodbyeRealProxy : RealProxy
    {
        public DontSayGoodbyeRealProxy(Type type) : base(type) { }

        public override IMessage Invoke(IMessage msg)
        {
            var ctorMsg = msg as IConstructionCallMessage;
            if (ctorMsg == null) throw new NotSupportedException();

            var o = InitializeServerObject(ctorMsg);
            GC.SuppressFinalize(GetUnwrappedServer());
            return o;
        }
    }

    sealed class DontSayGoodbyeAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type type)
        {
            return (MarshalByRefObject) new DontSayGoodbyeRealProxy(type).GetTransparentProxy();
        }
    }
}

Нет предела извращенству!
Re[3]: эээ?
От: Sinix  
Дата: 21.09.10 11:13
Оценка: 5 (1)
Здравствуйте, Константин Л., Вы писали:

КЛ>из-за protected? каков механизм?

Противоречащий C# Language Specification Version 4.0

10.13 Destructors
...
Destructors are implemented by overriding the virtual method Finalize on System.Object. C# programs are not permitted to override this method or call it (or overrides of it) directly. For instance, the program
class A
{
override protected void Finalize() {} // error
public void F() {
this.Finalize(); // error
}
}
contains two errors.
The compiler behaves as if this method, and overrides of it, do not exist at all. Thus, this program:
class A
{
void Finalize() {} // permitted
}
is valid, and the method shown hides System.Object’s Finalize method.



UPD: Также см ответ выше от Пельмешко.
Re[4]: эээ?
От: Mab Россия http://shade.msu.ru/~mab
Дата: 21.09.10 13:49
Оценка: 174 (8) +1
Здравствуйте, Пельмешко, Вы писали:


П>А в рантайме вызов финализера, видимо, просто молча валится на этапе JIT'а...

Не думаю, что дело в неверифицируемости этого кода. Все остается в силе, даже если написать
class DontSayGoodbye 
{
    protected virtual void Finalize() {}
}


Дело в другом. Объявляя в DontSayGoodbye вирутальный метод с именем Finalize мы заодно скрываем базовый object.Finalize. Убедиться в этом легко, запустив ildasm -- у метода будет стоять флаг newslot. (Кстати, компилятор обычного в таких случаях предупреждения не выдаст, а явно поставленный модификатор new сочтет избыточным.)

Соответственно, переопределение Finalize, которое породит компилятор для Goodbye, будет перекрывать (override) имеенно DontSayGoodbye.Finalzie, а не object.Finalize. Вызов же финализатора, конечно, делается через токен object.Finalize (как самый обычный вирутальный вызов), так что сработает object.Finalize, который пуст.

Итого, после того, как в базовом классе Finalize был скрыт, никаких шансов у производного его переопределить не осталось.

Поправь, если я ошибаюсь.
Re[5]: эээ?
От: Пельмешко Россия blog
Дата: 21.09.10 13:56
Оценка:
Здравствуйте, Mab, Вы писали:

Mab>Поправь, если я ошибаюсь.


Да, я чушь написал, конечно же рантайм вызывать будет по токену object.Finalize()

В Connect сообщили?
Re[6]: эээ?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 21.09.10 14:07
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>В Connect сообщили?


Я отправлял какие-то баги из этой области. Их отказались фиксить ввиду малой важности.
Re[7]: эээ?
От: _FRED_ Черногория
Дата: 21.09.10 14:09
Оценка: +1
Здравствуйте, nikov, Вы писали:

П>>В Connect сообщили?


N>Я отправлял какие-то баги из этой области. Их отказались фиксить ввиду малой важности.


И правильно: важность и правда не так что б уж очень, зато сколько удовольствия общественности!
Help will always be given at Hogwarts to those who ask for it.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.