【问题标题】:Casting to Interface (Dynamic casting at runtime)转换为接口(运行时动态转换)
【发布时间】:2017-12-20 16:37:08
【问题描述】:

我有 3 个类媒体,图像,信息。所有三个类都包含实现 IResourceModel 接口的文件列表。这些媒体、图像和信息文件被序列化并添加到字典中。我在 Dictionary 上循环,在运行时如何将这些对象(媒体、图像和 InfoFile)转换为 IResourceModel 并获取 Files 属性。

public interface IResourceModel<T>  {
    T Files { get; set; }
}

class Media : IResourceModel<MediaFiles>
{
   public MediaFiles Files { get; set; }
}

class Image : IResourceModel<ImagesFiles>
{
   public ImagesFiles Files{ get; set; }
}

class InfoFiles : IResourceModel<InfoFiles>
{
   public InfoFiles Files{ get; set; }
}

Dictionary<ResourceType, object> resourcesList = new Dictionary<ResourceType, object> {
                    { ResourceType.Media,Media},
                    { ResourceType.Image,Image},
                    { ResourceType.InfoFiles , InfoFile}
};

【问题讨论】:

  • 问题是 ImageFiles、InfoFiles 和 MediaFiles 与我所看到的无关。它们是否共享一个通用接口或基类?
  • 是 ImageFiles、InfoFiles 和 MediaFiles 包含两个相似的属性 1) FileId 2) Hash

标签: c# class generics interface


【解决方案1】:

您的类型层次结构的问题是没有IResourceModel。只有一个IResourceModel&lt;T&gt;,其中T 是具体类型。对于泛型类型,IResourceModel&lt;A&gt;IResourceModel&lt;B&gt; 是任何不同类型参数 AB 的两个离散类型,它们实际上除了相似的名称之外没有任何共享。因此,您根本无法将 MediaImageInfoFiles 转换为通用类型(对象除外),因为没有通用类型。

这通常通过引入另一种额外实现的非泛型类型来解决。例如,实现 IEnumerable&lt;T&gt; 的类型也实现了非泛型 IEnumerable 以允许非泛型迭代。

所以您的类型层次结构可能如下所示:

public interface IResourceModel
{
    IFiles Files { get; }
}
public interface IResourceModel<T> : IResourceModel
{
    new T Files { get; set; }
}

public class Media : IResourceModel<MediaFiles>
{
    public MediaFiles Files { get; set; }
    IFiles IResourceModel.Files => Files;
}
public class Image : IResourceModel<ImagesFiles>
{
    public ImagesFiles Files { get; set; }
    IFiles IResourceModel.Files => Files;
}
public class Info : IResourceModel<InfoFiles>
{
    public InfoFiles Files { get; set; }
    IFiles IResourceModel.Files => Files;
}

public interface IFiles {}
public class MediaFiles : IFiles { }
public class ImagesFiles : IFiles { }
public class InfoFiles : IFiles { }

现在您可以将类型转换为 IResourceModel 并访问 Files 属性以遍历所有 IFiles

【讨论】:

    【解决方案2】:

    您需要为ImageFilesInfoFilesMediaFiles 定义一个评论接口/类。一个例子如下。

    interface IFiles
    {
        string myField { get; set; }
        int myHash { get; set; }
    }
    
    class MediaFiles : IFiles
    {
        public string myField { get; set; }
        public int myHash { get; set; }
    }
    

    然后您可以通过以下方式迭代您的字典

    Dictionary<ResourceType, IResourceModel<IFiles>> resourcesList = new Dictionary<ResourceType, IResourceModel<IFiles>> {
        { ResourceType.Media, new Media {Files = new MediaFiles {myHash = 20203, myField = "MediaFiles"} } }
    };
    
    foreach (KeyValuePair<ResourceType, IResourceModel<IFiles>> entry in resourcesList)
    {
        var files = entry.Value.Files;
        var field = files.myField;
        var hash = files.myHash;
    }
    

    编辑

    理论上你可以使用dynamic而不是object来访问你想要的,即File。但我总是会做类似上述的事情,总是强制执行强类型。


    编辑 2

    要转换通用接口,需要另一个接口实现。

    public class Media : IResourceModel<MediaFiles>, IResourceModel<IFiles>
    {
        public MediaFiles Files { get; set; }
        IFiles IResourceModel<IFiles>.Files
        {
            get { return Files; }
            set { Files = (MediaFiles)value; } // <--- Cautious! Check type in production code.
        }
    }
    

    编辑 3

    重新思考您遇到的基本问题,即您为什么需要字典 resourcesList?即你的资源不应该给你它所属的类型,而不是你为它们保留一个映射吗?这促使我给出以下完整的重新实现。

    • ResourceTypeFiles 的属性。

    首先,你想在最后实现什么

    var listOfFiles = new List<IFiles> // <-- List rather than Dictionary
    {
        new ImagesFiles(
            new List<ImagesFile>
            {
                new ImagesFile {path = "C:\\wf.n"},
                new ImagesFile {path = "C:\\wfz.n"}
            }
        ),
        new MediaFiles(
            new List<MediaFile>
            {
                new MediaFile {path = "C:\\wf.jpg", foo = 1},
                new MediaFile {path = "C:\\wfz.png", foo = 2}
            }
        )
    };
    
    foreach (var files in listOfFiles)
    {
        Console.WriteLine(files.resourceType); // <-- Gives Media / Image
        var fileshash = files.GetHashCode();
        foreach (IFile file in files.GetFiles())
        {
            var myPath = file.path;
            var hash = file.GetHashCode();
        }
    }
    

    接口定义

    public interface IFile
    {
        string path { get; set; } // <--- GetHashCode doesn't need to be in here.
    }
    
    public interface IFiles : IEnumerable<IFile>
    {
        IEnumerable<IFile> GetFiles();
        ResourceType resourceType { get; } // <-- Getter only here on the interface
    }
    
    public interface IFiles<out T> : IFiles
        where T : IFile  // <-- This will enforce the same file type in this collection
    {
        new IEnumerable<T> GetFiles();
    }
    

    类定义

    public class ImageFile : IFile
    {
        public string path { get; set; }
    
        public override int GetHashCode()
        {
            return path.GetHashCode();
        }
    }
    
    public class ImageFiles : IFiles<ImageFile>
    {
        public ImageFiles(IEnumerable<ImageFile> files)
        {
            this.files = files.ToList();
        }
        public bool mySpecialProperty { get; set; } // <--- ImageFiles special, Not in Media nor Image
    
        public ResourceType resourceType => ResourceType.Image;
    
        private List<ImageFile> files;
    
        public IEnumerable<IFile> GetFiles()
        {
            return files;
        }
    
        IEnumerable<ImageFile> IFiles<ImageFile>.GetFiles()
        {
            return files;
        }
    
        public IEnumerator<IFile> GetEnumerator()
        {
            return files.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public override int GetHashCode()
        {
            return files.GetHashCode();
        }
    }
    

    【讨论】:

    • 我怎么能和Dynamics一样,请指教
    • 如果是dynamic,可以直接求Lists。请参阅编辑 2 了解您的演员无效问题。请参阅编辑 3 重新考虑您的问题。
    猜你喜欢
    • 2017-08-21
    • 2012-08-28
    • 2012-09-06
    • 1970-01-01
    • 2014-06-22
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    • 2014-10-13
    相关资源
    最近更新 更多