获取 List<T> 的现有实例

Get existing instance of List<T>

我继承了一个使用巨大 class 作为内存数据库的臃肿项目:

public class Database
{
    public class Parameter1
    {
        public string Code { get; set; }
        public string Label { get; set; }
        public List<Parameter1Value> paramValues;
    }

    public class Parameter2
    {
        public string Code { get; set; }
        public string Label { get; set; }
        public List<Parameter2Value> paramValues;
    }

    public class Parameter1Value
    {
        public string Value { get; set;}
        public Parameter parameter { get; set;}
    }

    public class Parameter2Value
    {
        public int Value { get; set;}
        public Parameter2 parameter { get; set;}
    }

    public List<Parameter1> parameter1List { get; set; }
    public List<Parameter2> parameter2List { get; set; }
}

我正在创建一个通用方法来创建 Parameter1 或 Parameter2 的实例(见下文)并将它们添加到它们各自的列表中,但我不知道如何使用这些类型从中获取 parameter1List 或 parameter2List 实例我的数据库 class。对于每个定义的类型,数据库 class 只包含一个列表 <T> 属性。这可能吗?

这是用于创建实例的通用方法:

public static Database Add<T>(this Database database, string code, string label) where T : new()
{
    T itemToCreate = (T)Activator.CreateInstance(typeof(T));
    itemToCreate.Code = code;
    itemToCreate.Label = label;
    var listForItem = database.GetList<T>; // This is the missing functionality
    listForItem.Add(itemToCreate);
    return database;
}

这是一个使用接口和通用约束的解决方案。

创建一个接口来表示通用参数class并根据需要向接口添加成员:

public interface IParameter { ... }

以及表示参数列表的接口:

public interface IParameterList<TParameter> where TParameter : IParameter
{
    List<TParameter> ParameterList { get; set; }
}

DatabaseParameter classes 实现这些新接口:

public class Parameter1 : IParameter

public class Parameter2 : IParameter

public class Database : IParameterList<Parameter1>, IParameterList<Parameter2>
{
    List<Parameter1> IParameterList<Parameter1>.ParameterList { get => parameter1List; set => parameter1List = value; }
    List<Parameter2> IParameterList<Parameter2>.ParameterList { get => parameter2List; set => parameter2List = value; }

    ...
}

向通用参数工厂函数添加一个 where TParameter : IParameter 约束,并让工厂函数需要一个 IParameterList<TParameter> 类型的参数,它是 Database [=39= 的一个实例].这满足了 Database class 拥有 TParameter 列表的编译器。现在我们只需 db.ParameterList.Add(r) 将我们的新参数添加到正确的列表中。

public static TParameter CreateParameter<TParameter>(IParameterList<TParameter> db) where TParameter : IParameter, new()
{
    var r = new TParameter(); // This is the generic function you mentioned. Do stuff here to create your Parameter class.
    db.ParameterList.Add(r); // Add the newly created parameter to the correct list
    return r;
}

代码转储(完整工作版本,在我拿起你的编辑后添加了通用工厂功能):

public class Parameter1 : IParameter
{
    public string Code { get; set; }
    public string Label { get; set; }
    public List<Parameter1Value> paramValues;
}

public class Parameter2 : IParameter
{
    public string Code { get; set; }
    public string Label { get; set; }
    public List<Parameter2Value> paramValues;
}

public class Parameter1Value
{
    public string Value { get; set; }
    public Parameter parameter { get; set; }
}

public class Parameter2Value
{
    public int Value { get; set; }
    public Parameter2 parameter { get; set; }
}

public class Database : IParameterList<Parameter1>, IParameterList<Parameter2>
{
    // Note: Setters for the List properties probably not needed here or in IParameterList as with the following code we instantiate them at class construction time and, in this MCVE at least, there are no further assignments
    public List<Parameter1> parameter1List { get; set; } = new List<Parameter1>();
    public List<Parameter2> parameter2List { get; set; } = new List<Parameter2>();

    List<Parameter1> IParameterList<Parameter1>.ParameterList { get => parameter1List; set => parameter1List = value; }
    List<Parameter2> IParameterList<Parameter2>.ParameterList { get => parameter2List; set => parameter2List = value; }

    public static TParameter Add<TParameter>(IParameterList<TParameter> db, string code, string label) where TParameter : IParameter, new()
    {
        var itemToCreate = new TParameter();
        itemToCreate.Code = code;
        itemToCreate.Label = label;
        db.ParameterList.Add(itemToCreate); // Add the newly created parameter to the correct list
        return itemToCreate;
    }
}

public interface IParameter
{
    string Code { get; set; }
    string Label { get; set; }
}

public interface IParameterList<TParameter> where TParameter : IParameter
{
    List<TParameter> ParameterList { get; set; }
}

// Testing:
void Main()
{
    var db = new Database();
    Database.Add<Parameter1>(db, "hello", "hello2");
    Database.Add<Parameter1>(db, "hello", "hello2");
    Database.Add<Parameter2>(db, "hello", "hello2");
    Console.WriteLine($"P1 count (should be 2): {db.parameter1List.Count()}; P2 count (should be 1): {db.parameter2List.Count}");
}

输出:

P1 count (should be 2): 2; P2 count (should be 1): 1

下面是一个使用泛型和反射获取目标列表的方案:

public static List<T> GetList<T>(this Database dataBase) where T : new()
{
    return dataBase.GetType()
      .GetProperties()
      .Where(x => x.PropertyType == typeof(List<T>))
      .Select(x => (List<T>)x.GetValue(dataBase))
      .FirstOrDefault();
}

图片来源:评论中的 Michael Randall