简体   繁体   English

如何创建一个 List(of T),其中 T 是 (T) 的接口,List 的内容继承了一个实现 T 接口的抽象类

[英]How can I create a List(of T) where T is an interface of (T) and the contents of the List inherit an Abstract class that implements the interface of T

I am attempting to implement some generic config types and have bumped into an issue with conversions.我正在尝试实现一些通用配置类型,但遇到了转换问题。 Ideally what I'd like is a strongly typed instance of configuration values that can be assigned to our different Exporters.理想情况下,我想要的是一个强类型的配置值实例,可以分配给我们不同的导出器。

What I have is:我所拥有的是:

  • IConfigurableExport : Interface to mark exporter as configurable and contains a ICollection<IExportConfiguration<Object>> Configurations property IConfigurableExport :将导出器标记为可配置的接口,并包含ICollection<IExportConfiguration<Object>> Configurations属性
  • IExportConfiguration<T> : Interface to define common properties (Name, Description, etc.) IExportConfiguration<T> :定义通用属性(名称、描述等)的接口
  • ExportConfiguration<T> : Abstract class that implements IExportConfiguration<T> , sets the interface properties as Abstract, adds some common functionality like setting the value of itself from a list of configuration values and whatnot ExportConfiguration<T> :实现IExportConfiguration<T>抽象类,将接口属性设置为 Abstract,添加一些常用功能,例如从配置值列表中设置自身的值等等
  • ConfigurationInstance<bool> : Inherits ExporterConfiguration<Boolean> ConfigurationInstance<bool> :继承ExporterConfiguration<Boolean>

The UI to configure the exporters is dynamically built based on the values from IConfigurableExport.Configurations.用于配置导出器的 UI 是基于 IConfigurableExport.Configurations 中的值动态构建的。 When I try to call IConfigurableExport.Configurations I get a casting error.当我尝试调用 IConfigurableExport.Configurations 时,出现转换错误。 Here is some sample code to illustrate the problem:下面是一些示例代码来说明问题:

using System;
using System.Linq;
using System.Collections.Generic;
                    
public class Program
{
    public static void Main()
    {
        var t = new MyExporter();
        var configs = t.Configurations;
        foreach(var conf in configs){
            Console.WriteLine(conf.Name);
        }
    }
}

public interface IExportConfiguration<T>{ 
    string Description {get;}
    Guid Id {get;}
    string Name {get;}
    T Value {get; set;}
}


public abstract class ExportConfiguration<T>
    : IExportConfiguration<T>
{
    public abstract string Description { get; }
    public abstract string Name { get; }
    public abstract Guid Id { get; }
    public abstract T Value { get; set; }
    public abstract T DefaultValue { get; set; }
    
    public virtual void SetValue(ICollection<KeyValuePair<Guid, object>> values)
    {
        if (values.Any(kvp => kvp.Key == this.Id))
            Value = (T)values.First(kvp => kvp.Key == this.Id).Value;
        else
            Value = this.DefaultValue == null ? this.DefaultValue : default(T);
    }
}

public interface IConfigurableExport {
    ICollection<IExportConfiguration<object>> Configurations {get;}
}

public class MyConfig : ExportConfiguration<bool> {
    public override string Description { get { return "This is my description"; }}
    public override string Name { get{ return "My Config Name"; }}
    public override Guid Id { get { return Guid.Parse("b6a9b81d-412d-4aa8-9090-37c9deb1a9f4"); }}
    public override bool Value { get; set;}
    public override bool DefaultValue { get; set;}
}

public class MyExporter : IConfigurableExport {
    MyConfig conf = new MyConfig();
    public ICollection<IExportConfiguration<object>> Configurations {
        get {
            return new List<IExportConfiguration<object>> { (IExportConfiguration<object>)conf };
        }
    }
}

Here is a dontnetfiddle to illustrate the problem这是一个 dontnetfiddle来说明这个问题

The main problem is that IExportConfiguration<T> defines a T Value {get; set;}主要问题是IExportConfiguration<T>定义了一个T Value {get; set;} T Value {get; set;}

If you have an IExportConfiguration<bool> , you can't cast IExportConfiguration<bool> to an IExportConfiguration<object> because you can't set Value with a type of object , it has to be set with a type of bool in order for it to be type safe.如果您有IExportConfiguration<bool> ,则不能将IExportConfiguration<bool> IExportConfiguration<object>转换为IExportConfiguration<object>因为您无法使用object类型设置Value ,必须使用bool类型设置它才能它是类型安全的。

To be able to make that kind of cast you would need to use a covariant interface like below为了能够进行这种类型的转换,您需要使用如下所示的协变接口

public interface IExportConfiguration<T> : IReadOnlyExportConfiguration<T>{
    new T Value {get; set;}
}

//this interface is covariant, and if you have a IReadOnlyExportConfiguration<string>
//it can be cast to an IReadOnlyExportConfiguration<object>
public interface IReadOnlyExportConfiguration<out T>{ 
    string Description {get;}
    Guid Id {get;}
    string Name {get;}
    T Value {get;}
}

and then you can redefine your configurable export as然后您可以将您的可配置导出重新定义为

public interface IConfigurableExport {
    ICollection<IReadOnlyExportConfiguration<object>> Configurations {get;}
}

But then you can't use MyConfig as a bool config because IReadOnlyExportConfiguration<bool> will not cast to IReadOnlyExportConfiguration<object> because bool is a simple data type.但是你不能使用MyConfig作为 bool 配置,因为IReadOnlyExportConfiguration<bool>不会转换为IReadOnlyExportConfiguration<object>因为 bool 是一种简单的数据类型。 The type of T has to be a class type in order for it to be covariant with object . T 的类型必须是类类型才能与object协变。

public class MyConfig : ExportConfiguration<string> {
    public override string Description { get { return "This is my description"; }}
    public override string Name { get{ return "My Config Name"; }}
    public override Guid Id { get { return Guid.Parse("b6a9b81d-412d-4aa8-9090-37c9deb1a9f4"); }}
    public override string Value { get; set;}
    public override string DefaultValue { get; set;}
}

And then finally your exporter becomes然后最后你的出口商变成

public class MyExporter : IConfigurableExport {
    MyConfig conf = new MyConfig();
    public ICollection<IReadOnlyExportConfiguration<object>> Configurations {
        get {
            return new List<IReadOnlyExportConfiguration<object>> { (IReadOnlyExportConfiguration<object>)conf };
        }
    }
}

But at this point you won't be able to set values on your configurations, and if that's needed, then you will need to restructure your solution.但此时您将无法为您的配置设置值,如果需要,您将需要重新构建您的解决方案。

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

相关问题 通用方法,其中 T 是实现接口的列表 - Generic Method where T is List that implements interface 无法在C#中将实现接口的类的实例添加到所述接口的List中 - Can't add instance of class that implements an interface to a List of said interface in C# 如何定义一个接口方法,其不同的实现可以返回<t> , 列表<t> , 列表<list<t> &gt; 其中 T 是自定义 class </list<t></t></t> - How can I define an interface method whose different implementation can return <T>, List<T>, List<List<T>> where T is a custom class T实现接口的通用方法<T> - Generic method where T implements Interface<T> 实现接口B的接口D的列表无法识别为列表 - List of interface D that implements interface B Can't be recognized as List<B> 为什么我不能从私有类/接口继承? - Why can't I inherit from a private class / interface? 创建IRepository列表 <T> 在界面中 - Create List of IRepository<T> in an interface 接口/抽象类IEnumerable <T> T是特定的方法 - Interface/Abstract Class IEnumerable<T> Method Where T is Specific BindingList <T>其中T是实现其他接口的接口 - BindingList<T> where T is interface that implements other interface 我的抽象类实现了一个接口,但没有实现它的一些方法。 我如何编译? - My abstract class implements an interface but doesn't implement some of its methods. How do I make it compile?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM