首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于事件的输入文件管理系统

基于事件的输入文件管理系统
EN

Code Review用户
提问于 2018-03-11 15:34:28
回答 1查看 100关注 0票数 2

我目前正在开发一个系统,它可以接受不同类型的文件,有些类只对特定类型的文件感兴趣,而有些则根本不区分。

Note

  • 文件类型不一定是文件扩展名的不同,而是它们的工作方式,例如,音乐文件具有持续时间,但不是只有一个扩展名,因此,每个只有持续时间的文件都被视为一个音乐文件,而不管其扩展名如何。

我决定采用一种基于事件的方法,因为可能没有使用者,而且可能有很多,我真的不想在通知器类中引入任何依赖项,也不想用不必要的逻辑干扰它们。

为了简化通知器的工作,我编写了一个助手类- ManagerHelper,这避免了在每个类中过滤输入的需要,并且在系统中添加了新的文件类型时也提供了扩展性,否则系统中会需要对每个通知程序类进行更改。

这是当前这些类的工作原理图:

足够简单的结构,它就能完成任务。

IFileInformation

所有文件类型都继承具有文件可能具有的最基本信息的公共接口:

代码语言:javascript
复制
public interface IFileInformation
{
    string FileName { get; }
    FileInfo FileInfo { get; }
    Uri Uri { get; }
}

FileInformation

接口的基本实现是FileInformation,它主要用于文本文档等文件:

代码语言:javascript
复制
public class FileInformation : IFileInformation
{
    public string FileName { get; }
    public FileInfo FileInfo { get; }
    public Uri Uri { get; }

    public FileInformation(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
        {
            throw new ArgumentNullException(nameof(filePath));
        }
        FileInfo = new FileInfo(filePath);
        FileName = Path.GetFileNameWithoutExtension(FileInfo.Name);
        Uri = new Uri(FileInfo.FullName);
    }

    public FileInformation(Uri fileUri)
        : this(fileUri.OriginalString)
    {
    }
}

MediaFileInformation

对于媒体文件,我们有MediaFileInformation,它有一个TimeSpan FileLength属性,还有一个属性+ DependencyProperty,用于文件播放/不播放的状态:

代码语言:javascript
复制
public class MediaFileInformation : DependencyObject, IFileInformation, INotifyPropertyChanged
{
    public TimeSpan FileLength { get; }
    public string FileName { get; }
    public FileInfo FileInfo { get; }
    public Uri Uri { get; }

    public static readonly DependencyProperty IsPlayingProperty =
        DependencyProperty.Register(nameof(IsPlaying), typeof(bool), typeof(MediaFileInformation),
            new PropertyMetadata(null));

    public bool IsPlaying
    {
        get => (bool)GetValue(IsPlayingProperty);
        set
        {
            SetValue(IsPlayingProperty, value);
            OnPropertyChanged();
        }
    }

    public MediaFileInformation(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
        {
            throw new ArgumentNullException(nameof(filePath));
        }
        FileInfo = new FileInfo(filePath);
        FileName = Path.GetFileNameWithoutExtension(FileInfo.Name);
        FileLength = FileInfo.GetFileDuration();
        Uri = new Uri(FileInfo.FullName);
    }

    public MediaFileInformation(Uri fileUri)
        : this(fileUri.OriginalString)
    {
    }

    #region INotifyPropertyChanged Implementation

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

管理器

实际上只有一个通用的静态类-- Manager<TFileInformation>。它包含一个单一事件,以及很少有相同方法的重载来请求新的文件更新:

代码语言:javascript
复制
public static class Manager<TFileInformation>
    where TFileInformation : IFileInformation
{
    public static event EventHandler<ManagerEventArgs<TFileInformation>> NewRequest;

    public static void Request(IEnumerable<TFileInformation> selectedFiles)
    {
        Request(new ManagerEventArgs<TFileInformation>(selectedFiles));
    }

    public static void Request(ManagerEventArgs<TFileInformation> args)
    {
        OnNewRequest(args);
    }

    private static void Request(IEnumerable<object> selectedFiles)
    {
        Request(selectedFiles.Cast<TFileInformation>());
    }

    private static void OnNewRequest(ManagerEventArgs<TFileInformation> args)
    {
        NewRequest?.Invoke(typeof(Manager<TFileInformation>), args);
    }
}

ManagerHelper

如图所示,该类服务器作为中介,通知每个Manager以及适当的信息,它们必须调用各自的事件。如果您已经注意到,在private类中有一个奇怪的Manager方法,它在这里使用,因为我试图将IEnumerable<object>的泛型类型参数转换为IFileInformation的特定实现时遇到了一些问题。

我想出的唯一解决方案,即保持Manager类的类型安全性,是创建一个私有方法,并仅通过反射访问该方法,并允许Manager类将IEnumerable<object>转换到适当的IEnumerable<TFileInformation>

代码语言:javascript
复制
public static class ManagerHelper
{
    private static readonly Dictionary<Type, Action<IEnumerable<object>>> _newRequests;

    static ManagerHelper()
    {
        var fileInformations = typeof(IFileInformation).GetDerivedTypesFor(Assembly.GetExecutingAssembly());

        _newRequests = new Dictionary<Type, Action<IEnumerable<object>>>();
        foreach (var information in fileInformations)
        {
            var instance = typeof(Manager<>).MakeGenericType(information);
            var methodInfo = instance.GetMethod("Request", BindingFlags.NonPublic | BindingFlags.Static);
            _newRequests.Add(information, enumerable => methodInfo.Invoke(instance, new object[] { enumerable }));
        }
    }

    public static void Request(IEnumerable<IFileInformation> selectedFiles)
    {
        Request(new ManagerEventArgs<IFileInformation>(selectedFiles));
    }

    public static void Request(ManagerEventArgs<IFileInformation> args)
    {
        var typeGroups = args.SelectedFiles.GroupBy(information => information.GetType());
        foreach (var typeGroup in typeGroups)
        {
            _newRequests[typeGroup.Key].Invoke(typeGroup);
        }
    }
}

ManagerEventArgs

代码语言:javascript
复制
public class ManagerEventArgs<TFileInformation>
    where TFileInformation : IFileInformation
{
    public IEnumerable<TFileInformation> SelectedFiles { get; }

    public ManagerEventArgs(IEnumerable<TFileInformation> selectedFiles)
    {
        SelectedFiles = selectedFiles ?? throw new ArgumentNullException(nameof(selectedFiles));
    }
}

使用

现在,如果您希望在添加X类型的新文件时收到通知,只需执行以下操作:

代码语言:javascript
复制
Manager<X>.NewRequest+=...

为通知程序发送不同类型文件的集合也非常简单:

代码语言:javascript
复制
ManagerHelper.Request(files)

如果您只想发送筛选过的一堆文件,您也可以:

代码语言:javascript
复制
Manager<X>.Request(files.OfType<X>());

你有什么想法?有什么瑕疵吗?有什么我能改进的吗?也许你有更好的替代方案?

我个人认为可以改进命名,也许有一种更好的方法来访问Manager的公共方法,而不是仅仅为了这个用途而使用私有方法。

如果你需要任何额外的信息,请随时在评论中问我。

EN

回答 1

Code Review用户

发布于 2018-03-11 16:09:40

FileInformationMediaFileInformation中的重复代码

MediaFileInformation应该继承FileInformation以消除重复的代码。在ctor中只需调用base

代码语言:javascript
复制
public class FileInformation
{
    public string FileName { get; }
    public FileInfo FileInfo { get; }
    public Uri Uri { get; }

    public FileInformation(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
        {
            throw new ArgumentNullException(nameof(filePath));
        }
        FileInfo = new FileInfo(filePath);
        FileName = Path.GetFileNameWithoutExtension(FileInfo.Name);
        Uri = new Uri(FileInfo.FullName);
    }

    public FileInformation(Uri fileUri)
        : this(fileUri.OriginalString)
    {
    }
}
public class MediaFileInformation : FileInformation
{
    public TimeSpan FileLength { get; }
    public MediaFileInformation(string filePath)
        : base(filePath)
    {
        FileLength = FileInfo.GetFileDuration();
    }

    public MediaFileInformation(Uri fileUri)
        : base(fileUri)
    {
    }
}
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/189347

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档