繁体   English   中英

如何使方法在不同类型中可重用

[英]How to make method reusable with different types

我在Visual Studio 2015中使用MVVM Light来构建WPF应用程序。 代码中有一个方法,该方法在代码中稍作更改就重复了4次; 唯一的区别是要修改的ObservableCollection的类型以及在数据服务层上调用的方法。

这是方法,该方法返回StatusViewModel对象的ObservableCollection ,用于填充ComboBox StatusVm用于绑定到ComboBoxSelectedItem ,设置为集合中的第一项,并且为“空白”:

private async Task<ObservableCollection<StatusViewModel>> GetStatuses()
{
    var result = new ObservableCollection<StatusViewModel>();
    var blank = new StatusViewModel
    {
        StatusId = -1,
        Status = null,
        Description = null,
        IsActive = false,
        CreatedDate = DateTime.Now
    };
    result.Add(blank);
    var dataService = new MyDataService();
    foreach (var c in await dataService.GetStatuses())
        result.Add(c);
    StatusVm =
        result.SingleOrDefault(c => c.StatusId.Equals(-1));
    return result;
}

这是StatusVm的私有字段和公共属性:

private StatusViewModel _statusVm;
public StatusViewModel StatusVm
{
    get { return _statusVm; }
    set
    {
        if (Equals(value, _statusVm)) return;
        _statusVm = value;
        RaisePropertyChanged();
    }
}   

现在想象一下,以上又重复了3次,添加了3种VM类型! 如何使GetStatuses()成为可以采用不同视图模型类型并在数据服务上调用适当方法的方法? 谢谢。

更新 :这是另一种类型的属性和方法:

private MroViewModel_mroVm;
public MroViewModel MroVm
{
    get { return _mroVm; }
    set
    {
        if (Equals(value, _mroVm)) return;
        _mroVm = value;
        RaisePropertyChanged();
    }
}   

private async Task<ObservableCollection<MroViewModel>> GetMro()
{
    var result = new ObservableCollection<MroViewModel>();
    var blank = new MroViewModel
    {
        StatusId = -1,
        Status = null,
        Description = null,
        IsActive = false,
        CreatedDate = DateTime.Now
    };
    result.Add(blank);
    var dataService = new MyDataService();
    foreach (var c in await dataService.GetMro())
        result.Add(c);
    MroVm =
        result.SingleOrDefault(c => c.StatusId.Equals(-1));
    return result;
}

您将为通用属性创建接口。

internal interface IStatusViewModel {
        int StatusId { get; set; }
        string Status { get; set; }
        string Description { get; set; }
        bool IsActive { get; set; }
        DateTime CreatedDate { get; set; }
}

在您需要检索其状态的类中实现接口

internal class MroViewModel : IStatusViewModel {
        public int StatusId { get; set; }
        public string Status { get; set; }
        public string Description { get; set; }
        public bool IsActive { get; set; }
        public DateTime CreatedDate { get; set; }
}

将方法设为静态并传递服务函数,该服务函数将调用适当的方法以检索旧状态。

public static async Task<ObservableCollection<T>> GetStatuses<T>(
    Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction)
    where T : IStatusViewModel, new()
{
    var result = new ObservableCollection<T>();
    var blank = new T
    {
        StatusId = -1,
        Status = null,
        Description = null,
        IsActive = false,
        CreatedDate = DateTime.Now
    };
    result.Add(blank);

    var dataService = new MyDataService();
    foreach (var c in await retrieveStatusesAction(dataService))
        result.Add(c);

    // TODO Implement Expression<Func<TSource, TResult>> projection for assigning to VM
    StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1));

    return result;
}

然后,您将这样调用此方法:

GetStatuses((service) => service.GetMro());

我没有对此进行测试,因此需要使用表达式编译来分配StatusVm。 现在,我将看一下如何执行此操作,但是这个想法已经存在。

对于表达式和属性分配: 属性选择器Expression <Func <T >>。 如何获取/设置选定属性的值

-编辑-

对于VM分配,如下所示:

    public static async Task<ObservableCollection<T>> GetStatuses<T, TContainer>(
        TContainer instance,
        Expression<Func<TContainer, T>> viewModelProjection,
        Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction)
        where T : IStatusViewModel, new()
    {
        var result = new ObservableCollection<T>();
        var blank = new T
        {
            StatusId = -1,
            Status = null,
            Description = null,
            IsActive = false,
            CreatedDate = DateTime.Now
        };
        result.Add(blank);

        var dataService = new MyDataService();
        foreach (var c in await retrieveStatusesAction(dataService))
            result.Add(c);

        var vmStatus = result.SingleOrDefault(c => c.StatusId.Equals(-1));

        // Warning: Check casted values, this is unsafe
        var vm = (PropertyInfo)((MemberExpression)viewModelProjection.Body).Member;
        vm.SetValue(instance, vmStatus, null);

        return result;
    }

然后,您将这样调用方法:

await GetStatuses(this, inst => inst.MroVm, (service) => service.GetMro());

如果您不熟悉表达式,我将进行解释。 第一个参数是视图模型实例所在的对象。 第二个参数从该对象中选择与需要更改的视图模型相对应的属性。 最后一个参数是接受服务并返回检索状态的适当方法的函数-就像C ++中指向该函数的指针一样。

这将编译,但不确定是否会按预期运行。 这应该。 如果您有任何问题,请在评论中写下来。

您可以定义接口并尝试使用StrategyFactory的组合,如下所示(为简单起见,我跳过了async / await):

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Starting");
        var mainVm = new MainViewModel();
        mainVm.GetStatuses();
        mainVm.GetMro();

        Console.WriteLine("Status: {0} {1}", mainVm.StatusVm.Name, mainVm.StatusVm.CreateDate);
        Console.WriteLine("MroVm: {0} {1}", mainVm.MroVm.Name, mainVm.MroVm.CreateDate);
    }
}

public class MainViewModel
{
    public StatusViewModel StatusVm { get; set; }
    public MroViewModel MroVm { get; set; }

    public void GetStatuses()
    {
        var result =  Get(VmKind.Status);
        StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as StatusViewModel;
    }
    public void GetMro()
    {
        var result = Get(VmKind.Mro);
        MroVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as MroViewModel;
    }

    public IEnumerable<IVm> Get(VmKind vmKind)
    {
        var dataService = new MyDataService();
        return dataService.Get(vmKind);
    }


}

public interface IVm
{
    int StatusId { get; set; }
    DateTime CreateDate { get; set; }
    string Name { get; }
}

public class StatusViewModel : IVm
{
    public DateTime CreateDate { get; set; }
    public int StatusId { get; set; }
    public string Name { get { return "StatusViewModel"; } }
}

public class MroViewModel : IVm
{
    public DateTime CreateDate { get; set; }
    public int StatusId { get; set; }
    public string Name { get { return "MroViewModel"; } }
}

public enum VmKind    {Status, Mro }

#region Strategy

public interface IDataGetter
{
    IEnumerable<IVm> Get(VmKind vmKind);
}

public class MyDataService : IDataGetter {
    public IEnumerable<IVm> Get(VmKind vmKind)
    {
        switch (vmKind)
        {
            case VmKind.Status:
                return GetStatuses();
                //break;
            case VmKind.Mro:
                return GetMro();
                //break;
            default:
                throw new ArgumentException("Unknown VM type");
        }
    }

    private IEnumerable<IVm> GetMro()
    {
        return new List<MroViewModel> {
            new MroViewModel { StatusId = -1, CreateDate = DateTime.Now },
            new MroViewModel { StatusId = 2, CreateDate = DateTime.Now }
        };
    }

    private IEnumerable<StatusViewModel> GetStatuses()
    {
        return new List<StatusViewModel> {
            new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now },
            new StatusViewModel { StatusId = 2, CreateDate = DateTime.Now }
        };
    }
}
#endregion

#region Factory
public class VmFactory {
    static IVm Create(VmKind vmKind)
    {
        IVm result = null;
        switch (vmKind)
        {
            case VmKind.Status:
                result = new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now };
                break;
            case VmKind.Mro:
                result = new MroViewModel { StatusId = -1, CreateDate = DateTime.Now };
                break;
            default:
                throw new ArgumentException("Unknown VM type");
                //break;
        }
        return result;
    }
}
#endregion

我实际上没有在这里使用Factory,但是您可以使用它来轻松创建VM。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM