C#尝试使用通用类型的数组索引

时间:2016-05-19 16:06:33

标签: c# arrays generics

所以,我已经找到了一些问题的几个答案,但我试图结合的两件事似乎把事情放在一起,其他人已经遇到过(或者至少他们&) #39;不要发布它。)

我的目标是能够使用我的代码:

    ModelFactory[DataModelX].DataY

我知道我能得到的是:

    ModelFactory.Get<DataModelX>.DataY

不可否认,但是在这一点上,我觉得我已经接近并想知道我是否能够得到它。

我目前的课程是:

public class ModelFactory<T> :  : IReadOnlyList<IDataModel> where T : IDataModel
    private static ModelFactory<IDataModel> _instance;

    private ModelFactory() {}

    public static ModelFactory<IDataModel> ModelFactory => _instance ?? (_instance = new DataModelFactory<IDataModel>());

    private readonly List<IDataModel> _models = new List<IDataModel>();

    private T GetHelper(Type type)
    {
        // check if model exists
        foreach (var model in _models.Where(model => model.GetType() == type))
        {
            // model exists, return it
            return (T) model;
        }
        // model doesn't exist, so we need to create it
        var newModel = CreateModel(type);
        _models.Add(newModel);
        return newModel;
    }

    public T this[
        Type type
    ] {
        get
        {
            return GetHelper(type);
        }
    }

DataModels就像这样:

public class DataModel1 : IDataModel
    public string Alpha {get; set;}
    public int Beta {get; set;}
    // etc...

public class DataModel2 : IDataModel
    public foo Cat {get; set;}
    public foobar Dog {get; set;}
    // etc...

等等。

但是,当我尝试输入ModelFactory.ModelFactory[DataModelX].时,我无法访问ModelFactory。该物业显然是公开的,并包含在适当的类别中。另外,我不能将该类作为参数传递(当然),但我不知道如何调整数组索引上的参数以接受该类作为参数。

Constaints:

  1. ModelFactory应该是静态类或单例。
  2. DataModels是所有实现IDataModel的类。
  3. 数据属性对每个DataModel
  4. 都是唯一的

2 个答案:

答案 0 :(得分:2)

您可以使用typeof(T)来获取通用参数T的类型说明。

T newModel = (T)CreateModel(typeof(T));

如果它始终描述与T相同的类型,则无需额外的Type参数。

此外,您可以使用字典而不是列表来存储模型。

private readonly Dictionary<Type,IDataModel> _models = new Dictionary<Type,IDataModel>();

然后你可以用

获得一个值
IDataModel model;
If (!_models.TryGetValue(typeof(T), out model)) {
    model = CreateModel(typeof(T));
    _models.Add(typeof(T), model);
}
return (T)model;

您的类的泛型类型参数具有误导性,因为它似乎意味着您将为每个模型类型创建一个工厂。放下它。

此外,您无法实施IReadOnlyList<out T>,因为它需要索引为int。放弃它。

public class ModelFactory { ... }

现在您有两个选择:

  1. 让索引器或普通的Get方法返回IDataModel,然后转换结果

    var m = (MyModelType)factory.Get(typeof(MyModelType));

    var m = (MyModelType)factory[typeof(MyModelType)];

  2. 声明一个get method with a generic type argument(但该类没有泛型类型参数!)。索引器不能有泛型类型参数。

  3. public T Get<T>() : where T : IDataModel
    {
        ... (same as above)
        return (T)model;
    }
    

    调用它
    var m = factory.Get<MyModelType>();
    

答案 1 :(得分:1)

虽然拥有它会很好,但遗憾的是,你所要求的是不可能的。

实现这个工厂有三种可能的方法,其中没有一种可以做你想要的:

1)您拥有的实施,即通用ModelFactory<T> where T : IDataModel。如上所述,ModelFactory<T>会强制您限制工厂采用单一类型 - 例如,您必须定义ModelFactory<DataModel1>ModelFactory<DataModel2>等等。&# 39;无论如何都是一个值得怀疑的设计决定,但更重要的是,你只能投入并获得用于创建工厂的具体IDataModel类型。例如,您无法创建ModelFactory<DataModel1>并从索引器获取DataModel2。因此,使用此实现并没有多大意义。

2)非通用ModelFactory,其索引器返回IDataModel。这是完全有效的,但您必须转换输出以获得具体类型。因此,使用索引器的调用语法为((DataModel2)ModelFactory[DataModel2]).Cat(ModelFactory[DataModel2] as DataModel2).Cat。在我看来,这比一般的Get<T>()函数更加丑陋。

3)具有通用索引器的非通用ModelFactory,例如public T this<T>[Type type] where T : IDataModel。这与您想要的最接近,但不幸的是,它在当前(C#6)规则下明确禁止。例如,请参阅Why it is not possible to define generic indexers in .NET?。但是,值得注意的是,您仍然无法让编译器自动将type识别为T。您无法写public T this<T>[T type],因为这会将type解释为T类型的对象,也不能写public T this<T>[typeof(T) type]或类似的内容,因为{{1}是typeof(T)类型的对象。所以你必须用Type调用你的索引器(仍然很丑!),并在里面实现运行时检查以确保ModelFactory<DataModel2>[typeof(DataModel2)].Cat真的是type类型。

因此,您最干净的选择是使用非通用T,使用ModelFactory从内部IDataModel检索Dictionary<Type, IDataModel>实现。