Dispose pattern
От: BOleg Россия  
Дата: 28.08.07 11:26
Оценка: 35 (1)
Реализовал сабж.

Пример использования:
disposable class BaseClass
{
    dispose Dispose() : void
    {
        WriteLine("cleaning up BaseClass");
    }
}

class DerivedClass : BaseClass
{
    [Dispose]
    Dispose1() : void
    {
        WriteLine("cleaning up DerivedClass");
    }

//    [Dispose]    //Error: Dispose method already defined
//    Dispose2() : void
//    {
//    }
}

Main() : void
{
    def test=DerivedClass();

    dispose test;
    
    _=ReadLine();
}


Выведет:
cleaning up DerivedClass
cleaning up BaseClass

Код макросов:
[Nemerle.MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Class)]
macro Disposable (tb : TypeBuilder)
    syntax("disposable")
{
    tb.AddImplementedInterface(
        <[ System.IDisposable ]>
    );
}

[Nemerle.MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Method)]
macro Dispose (tb : TypeBuilder, mb : ParsedMethod)
    syntax ("dispose")
{
    def checkAlreadyDefined()
    {
        foreach (ClassMember.Function (_, _, _) as f in tb.GetParsedMembers (true))
            when(f.Name=="Dispose" && f.Attributes & NemerleAttributes.SpecialName != 0)
                Message.FatalError("Dispose method already defined");
    }

    def checkImplements(t : TypeInfo) : bool
    {
        | tc is TypeBuilder =>
            if(List.ForAll(tc.GetParsedMembers(true), x=>{
                    !(x is ClassMember.Function && x.Name == "_N_dispose");
                }))
                checkImplements(t.BaseType)
            else
                true
        | _ => false;
    }
    
    checkAlreadyDefined();

    def attrs=Modifiers(NemerleAttributes.SpecialName | NemerleAttributes.Protected, []);
    if (!checkImplements(tb)) 
    {
        tb.Define(
        <[ 
            decl: _N_dispose() : void implements System.IDisposable.Dispose
            { 
                this.Dispose(true);
                System.GC.SuppressFinalize(this);
            }
        ]>);

        attrs.mods |= NemerleAttributes.Virtual;
        tb.Define(
        <[
            decl: ..$attrs Dispose(IsDisposing : bool) : void 
            { 
                if(IsDisposing)
                    $(mb.Name : dyn) ();
                else
                    Finalize();
            } 
        ]>);
        
        tb.Define(
        <[
            decl: protected override Finalize() : void 
            { 
                this.Dispose(false);
            } 
        ]>);
    }
    else
    {
        attrs.mods |= NemerleAttributes.Override;
        tb.Define(
        <[
            decl: ..$attrs Dispose(IsDisposing : bool) : void 
            { 
                if (IsDisposing)
                {
                    try
                    {
                        $(mb.Name : dyn) ();
                    }
                    finally
                    {
                        base.Dispose(true);
                    }
                }
                else
                {
                    base.Dispose(false);
                }
            } 
        ]>);
    }
}

macro DisposeOperator(expr)
    syntax ("dispose", expr)
{
    <[ 
        maybe_valuetype_dispose($expr);
    ]>;
}


Есть пара нюансов:
1. Пришлось сделать дополнительный макрос Disposable, который тупо добавляет интерфейс IDisposable. Хотел сделать одним макросом, но наследоваться можно только в режиме MacroPhase.BeforeInheritance, а получать информацию о методах базовых классов — не ранее, чем в режиме MacroPhase.BeforeTypedMembers.
2. Хотел добавить синтаксический сахар и писать методы Dispose в виде ~this() { } — не вышло.
3. Оператор dispose выполняет проверку на null и уже тогда вызывает метод Dispose().
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.