1

我正在使用 Windsor 3.1.0 并LifestyleTransient使用Typed Factory实例化一个组件。正如引用页面中所说,我必须释放从工厂获得的每个对象。但是我处于这样的场景中

class MyTypedFactory
{
    MyTransientCommand CreateCommand();
    void Release(MyTransientCommand command);
}

class MyTransientCommand
{
    public void Execute() { }
}

class ClassA
{
    public ClassB CommandPopulator { get; set; }

    public void Foo()
    {
        List<MyTransientCommand> commands = new List<MyTransientCommand>();

        CommandsPopulator.Commands = commands;

        for (int i=0; i<100; ++i)
        {
            CommandsPopulator.Bar();
        }

        foreach (MyTransientCommand command in commands)
        {
            command.Execute();
        }
    }
}

class ClassB
{
    public MyTypedFactory Factory { get; set; }

    List<MyTransientCommand> Commands { get; set; }

    public void Bar()
    {
        Commands.Add(Factory.CreateCommand());
    }
}

这可能看起来过于复杂,但它说明了我的代码中的对象流。在这里,外部类 ( ClassB) 使用类型化工厂创建的命令填充commands队列。

当工厂不再需要它们时(在Execute循环之后)释放这些对象成为整个问题。我可以在循环中释放,但释放方法本身的异常是不安全的。

class ClassA
{
    public MyTypedFactory Factory { get; set; }

    ...

    public void Foo()
    {
        ...
        foreach (MyTransientCommand command in commands)
        {
            command.Execute();
        }

        foreach (MyTransientCommand command in commands)
        {
            Factory.Release(command);
        }
    }
}

如果其中一个 Release 调用抛出其余对象将不会被释放并会造成内存泄漏(我知道这不应该发生,但我试图防止这种事情发生)。

是否可以MyTransientCommand在创建后立即发布ClassB.Bar?在方法返回之前,GC 不会释放它,ClassA.Foo因为它包含对命令对象集合的引用。

如果进行 WCF 客户端调用并且 WCF 客户端是使用 Windsor 设置的,MyTransientCommand是否可以发布?或者更一般地说,如果使用温莎提供的其他组件?ClassB.BarMyTransientCommand.ExecuteMyTransientCommand.Execute

PS问题中的代码看起来是人为的和不自然的,确实如此。这是因为算法必须有副作用:在事务中更改数据库中的数据状态在某些情况下必须引发 WCF 调用。必须在事务完成后而不是在事务期间进行这些调用。

4

2 回答 2

0

通常我会依赖创建/注入对象的类来负责释放它,但它不应该导致以你描述的方式释放它的任何问题 - 你可以将发布代码放在finally.

foreach (var command in commands)
{
    try
    {
        command.Execute();
    }
    finally
    {
        Factory.Release(command);
    }
}

为了扩展一点,Castle 将注入所有必要的依赖项(只要您的工厂设置正确)。从容器中释放对象只是释放该实例。如果您在方法中修改注入的LifestyleSingleton对象,您可能会遇到并发问题Execute(),但这与从容器中释放它没有任何关系。

于 2012-11-14T03:30:48.740 回答
0

不,在使用之前发布组件是不行的。Windsor 跟踪嵌套组件的生命周期,因此释放根组件通常可能会释放它的子组件。而且我想避免使用状态可能无效的组件。

所以我最终采用了以下释放方式commands

class ClassA
{
    public MyTypedFactory Factory { get; set; }
    public ClassB CommandPopulator { get; set; }

    public void Foo()
    {
        List<MyTransientCommand> commands = new List<MyTransientCommand>();

        try
        {
            CommandsPopulator.Commands = commands;

            for (int i=0; i<100; ++i)
            {
                CommandsPopulator.Bar();
            }

            foreach (MyTransientCommand command in commands)
            {
                command.Execute();
            }
        }
        finally
        {
            foreach (MyTransientCommand command in commands)
            {
                try
                {
                    Factory.Release(command);
                }
                catch (Exception exception)
                {
                    Log(exception);
                }
            }
        }
    }
}

这样我可以肯定,如果在使用CommandsPopulator或执行命令时发生异常,所有命令仍将被释放。此外,每个命令都在 try/catch 块中释放,以防止因Release打破循环而引发异常。

希望 Windsor 遵循 no-throw 语义来使用 Typed Factory 释放对象,否则发生异常的机会非常低。

于 2012-11-15T17:03:28.730 回答