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

问题描述 投票:0回答:2

我有3个类媒体,图像,信息。所有三个类都包含实现IResourceModel接口的文件列表。这些Media,Image和InfoFile被序列化并添加到词典中。我正在循环使用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}
};
c# class generics interface
2个回答
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


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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.