模式与C#中的泛型参数匹配

时间:2017-07-26 15:38:09

标签: c# generics pattern-matching

在C#我有这样的情况:

class ModelBase<TKey>
{
    TKey Id { get; set; }
}

class Repo<TModel, TKey> :
    where TModel : ModelBase<TKey>
{
    //code where I access both TModel and TKey types
}

在我的Repo类中,我需要同时访问TModelTKey,但是当我特定TModel参数时,模式匹配系统应该能够提取{{ 1}}自动。

要使用TKey泛型类型,我必须将其声明为泛型参数,因此每次我需要创建一个Repo时,我必须使用特定的模型和相对键类型。我怎样才能删除显式键类型的需要,并通过模式匹配来提取它?在编译时使用验证的解决方案是首选。

2 个答案:

答案 0 :(得分:2)

由于两个原因,您在C#中无法提出要求:

  1. 构造函数中没有类型推断:

    class Foo<T>
    { 
        public Foo(T t) { ... }
    }
    
    var foo = new Foo(1); //compile time error, `int` will not be inferred
    

    这种限制很奇怪,并且与方法类型推断的工作方式完全不一致。构造函数不是方法,但应该

  2. 类型推断是全部或全部,没有中间立场:

    T Whatever<T, Q>(Q q) where Q: T { ... }
    

    您可能认为的任何假设语法中的部分类型推断根本不起作用:

    var blah = NewFoo<Blah>(q); //compile time error
    

    或者

    var blah = NewFoo<Blah,>(q); //compile time error
    

    将会失败。

  3. 为什么呢?好吧,因为语言是按照它的方式设计的。第一个限制可以解决,第二个限制我不会很快看到它发生,所以如果你打算等待它,请坐下。

答案 1 :(得分:1)

我认为对此的解决方法可能是声明一个非泛型类型,其中包含一个属性来存储主键类型。然后泛型类型可以继承,并在构造函数中设置属性。

这样的事情:

class ModelBase
{
    public Type KeyType { get; set; }
}

class ModelBase<TKey> : ModelBase
{
    public TKey Id { get; set; }

    public ModelBase()
    {
        KeyType = typeof(TKey);
    }
}

class Repo<TModel> where TModel : ModelBase, new()
{
    // code where I access both TModel and TKey types
    public void Test()
    {
        var modelType = typeof(TModel).Name;
        var keyType = new TModel().KeyType.Name;
        Console.WriteLine($"{modelType}     {keyType}");
    }
}

允许你编写这样的代码:

class MyIntEntity : ModelBase<int>
{
    public new int Id { get; set; }
}

class MyStringEntity : ModelBase<string>
{
    public new string Id { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var repoIntegerKey = new Repo<MyIntEntity>();
        var repoStringKey = new Repo<MyStringEntity>();

        repoIntegerKey.Test(); // prints "MyIntEntity        Int32"
        repoStringKey.Test();  // prints "MyStringEntity     String"
        Console.ReadLine();
    }        
}