转换为接口(运行时动态转换)

时间:2017-12-20 16:37:09

标签: c# class generics interface

我有3个类媒体,图片,信息。所有三个类都包含实现IResourceModel接口的文件列表。这些Media,Image& InfoFile被序列化并添加到Dictionary中。我正在循环使用Dictionary,在运行时如何将这些对象(Media,Image& InfoFile)转换为IResourceModel和fetch 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}
};

2 个答案:

答案 0 :(得分:0)

您需要为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();
    }
}

答案 1 :(得分:0)

您的类型层次结构存在的问题是没有IResourceModel。只有IResourceModel<T> T具体类型。对于通用类型,IResourceModel<A>IResourceModel<B>是任何不同类型参数AB的两种离散类型,它们实际上除了相似的名称之外不会共享任何内容。因此,您无法将MediaImageInfoFiles转换为公共类型(除了对象),因为没有常见类型。

这通常通过引入另外实现的另一种非泛型类型来解决。例如,实现IEnumerable<T>的类型也实现了非泛型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