每次调用方法时创建对象的新实例

时间:2013-06-08 02:14:58

标签: c#

Messenger.Default.Register<OpenWindowMessage>(this, message =>
{
    var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>();
    adventurerWindowVM.Adv = message.Argument;
    var adventurerWindow = new AdventurerView() 
    {
        DataContext = adventurerWindowVM
    };
    adventurerWindow.Show();
});

这段代码相当简单;它只是打开一个新窗口并设置新窗口的DataContext。我遇到的问题是,如果我执行两次,第一个实例的内容将被覆盖并设置为第二个实例的内容,因为adventurerWindowVM是两个窗口的DataContext,每次都被覆盖代码被调用。我正在寻找一种方法来防止这种情况;我希望能够使用此消息打开多个窗口并使每个窗口都是唯一的,但到目前为止我还没有想出办法。任何建议将不胜感激。我为这个模糊的标题道歉;我不确定这个问题的名称。 (另外,我知道这不是一种方法。这个代码块会被调用什么?)

更新:我正在使用MVVM Light,而我的代码是基于某个人在此答案中为我提供的示例:https://stackoverflow.com/a/16994523/1667020

以下是我的ViewModelLocator.cs

中的一些代码
public ViewModelLocator()
{
    _main = new MainViewModel();

    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    SimpleIoc.Default.Register<GameViewModel>();
    SimpleIoc.Default.Register<AdventurerViewModel>();
}

4 个答案:

答案 0 :(得分:8)

得到了另一个答案后,我想我可以说这里使用的IoC容器只是来自MvvmLight的SimpleIoC并且要在每个GetInstance(...)上获得一个新的VM实例,只需要做的就是每次尝试解析VM的实例时都会传入唯一的密钥。

所以你可以切换

var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>();

var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>(System.Guid.NewGuid().ToString());

然而正如MVVMLight Here的作者所提到的,这些VM将被缓存,我们需要在不再需要时删除它们。在您的情况下,可能在Window关闭时。

因此,我的整个lambda就像:

Messenger.Default.Register<OpenWindowMessage>(this, message =>
{
    var uniqueKey = System.Guid.NewGuid().ToString();
    var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>(uniqueKey);
    adventurerWindowVM.Adv = message.Argument;
    var adventurerWindow = new AdventurerView() 
    {
        DataContext = adventurerWindowVM
    };
    adventurerWindow.Closed += (sender, args) => SimpleIoc.Default.Unregister(uniqueKey);
    adventurerWindow.Show();
});

注意:

虽然这比使用( new AdventurerViewModel())自己创建一个新VM要长3行但我仍然喜欢这个,因为如果你使用IoC容器来管理VM的LifeTime,然后让它完全管理它们。不需要时,不要真的喜欢mix-n-match。而是让IoC Container保持其意图。

如果您需要更多地控制VM注入和生命周期管理,请查看更复杂的Ioc控制器,例如UnitySimpleIoC只是想让你的脚在IoC类容器中“湿润”,并且在这方面做得非常好。

答案 1 :(得分:2)

我认为您尝试使用多个视图的 ViewModel 实例。所以视图显然会覆盖彼此的viewmodel内容。

如果你这样做会怎么样;

        Messenger.Default.Register<OpenWindowMessage>(this, message =>
    {
        var adventurerWindowVM = new AdventurerViewModel();
        adventurerWindowVM.Adv = message.Argument;
        var adventurerWindow = new AdventurerView() 
        {
            DataContext = adventurerWindowVM
        };
        adventurerWindow.Show();
    });

答案 2 :(得分:1)

这是一个方法调用,使用lambda表达式传入一个匿名方法。

看起来你从某种IoC容器中获取了AdventurerViewModel。如何配置IoC容器?特别是,它给你回来的对象的范围是什么?例如,如果将IoC配置为在单例范围内创建对象,则每次都会返回对同一对象的引用。您可能需要在IoC容器中配置对象的范围,以便每次都为您提供新的副本。

如何操作取决于您的IoC容器。在不知道您正在使用哪个IoC框架或查看其配置的情况下,无法进行任何进一步的评论。

答案 3 :(得分:0)

我的建议是为 SimpleIOC 创建一个扩展方法。像这样:

public static T CreateInstance<T>(this SimpleIoc simpleIoc)
{
    // TODO implement
}

您已经知道获取相同实例的方法;使用创建新实例的方法扩展 SimpleIoc:

T instance = SimpleIoc.Default.GetInstance<T>();
T createdInstance = SimpleIoc.Defalt.CreateInstance<T>();

如果您不熟悉扩展方法,请参阅Extension Methods Demystified

实现:

  • T 类型,获取构造函数。
  • 如果有多个构造函数:要么抛出异常,要么决定使用哪个构造函数。简单方法:使用与 SimpleIoc.GetInstance 中使用的方法相同的方法,带有一个属性。更详细的方法:尝试找出是否可以找到与构造函数之一匹配的注册元素。此处不作解释。
  • 找到所需的构造函数后,获取其参数。
  • 向 SimpleIoc 询问此参数的实例,或者如果它们也应该是新的,请询问 SimpleIoc 创建新实例。
  • 创建实例

.

public static T CreateInstance<T>(this SimpleIoc ioc)
{
    return (T)ioc.CreateInstance(typeof(T));
}

public static object CreateInstance(this SimpleIoc ioc, Type type)
{
    ConstructorInfo constructor = ioc.GetConstructor(type);
    IEnumerable<object> constructorParameterValues = ioc.GetParameters(constructor);
    constructor.Invoke(constructorParameterValues.ToArray());
}

决定使用哪个构造函数:

private static ConstructorInfo GetConstructor(this SimpleIoc ioc, Type type)
{
    ConstructorInfo[] constructors = type.GetConstructors();
    ConstructorInfo constructorToUse;
    if (constructorInfo.Length > 1)
    {
        // Decide which constructor to use; not explained here
        // use Attribute like SimpleIoc.GetInstance?
        // other method: use SimpleIoc.IsRegistered to check which Parameters
        // are registered: use ConstructorInfo.GetParameters()
        constructorToUse = 
    }
    else
        constructorToUse = constructoInfo[0];
    return constructorToUse;
}

要在构造函数中获取参数的值,我们需要决定是从 Ioc 中获取现有值,还是创建新值:

public static IEnumerable<object> GetParameterValues(this simpleIoc ioc,
    ConstructorInfo constructor)
{
    IEnumerable<Type> parameterTypes = contructor.GetParameters()
        .Select(parameter => parameter.ParameterType);
    return ioc.GetInstances(parameterTypes);
}

public static IEnumerable<object> GetInstances(this SimpleIoc ioc,
    IEnumerable<Type> types)
{
    // TODO: decide if we want an existing instance from ioc,
    // or a new one

    // use existing instance:
    return types.Select(type => ioc.GetInstance(type));

    // or create a new instance:
    return types.Select(type => ioc.CreateInstance(type));
}

这看起来像很多代码,但大部分都是注释,大多数方法都是一行代码。