你能解释一下这种泛型行为吗?如果我有解决方法吗?

时间:2012-10-29 16:58:15

标签: c# oop generics implementation

以下示例程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericsTest
{
    class Program
    {
        static void Main(string[] args)
        {
            IRetrievable<int, User> repo = new FakeRepository();

            Console.WriteLine(repo.Retrieve(35));
        }
    }

    class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    class FakeRepository : BaseRepository<User>, ICreatable<User>, IDeletable<User>, IRetrievable<int, User>
    {
        // why do I have to implement this here, instead of letting the
        // TKey generics implementation in the baseclass handle it?
        //public User Retrieve(int input)
        //{
        //    throw new NotImplementedException();
        //}
    }

    class BaseRepository<TPoco> where TPoco : class,new()
    {
        public virtual TPoco Create()
        {
            return new TPoco();
        }

        public virtual bool Delete(TPoco item)
        {
            return true;
        }

        public virtual TPoco Retrieve<TKey>(TKey input)
        {
            return null;
        }
    }

    interface ICreatable<TPoco> { TPoco Create(); }
    interface IDeletable<TPoco> { bool Delete(TPoco item); }
    interface IRetrievable<TKey, TPoco> { TPoco Retrieve(TKey input); }
}

此示例程序代表我的实际程序使用的接口,并演示了我遇到的问题(在FakeRepository中注释掉)。我希望这个方法调用一般由基类处理(在我的实例中,它能够处理95%给出的情况),允许通过明确指定TKey的类型来覆盖子类。我对IRetrievable使用什么参数约束似乎并不重要,我永远不会让方法调用掉到基类。

另外,如果有人能够看到另一种方法来实现这种行为并获得我最终要寻找的结果,我会非常有兴趣看到它。

思想?

2 个答案:

答案 0 :(得分:3)

该代码无法编译,原因与此简单示例无法编译相同:

public interface IBar
{
    void Foo(int i);
}

public class Bar : IBar
{
    public void Foo<T>(T i)
    {
    }
}

方法根本没有相同的签名。是的,您可以致电someBar.Foo(5),它会将T解析为int,但事实仍然是Foo中的Bar仍然没有相同的签名作为一种实际以int作为参数的方法。

您可以通过在类型中同时使用非泛型和泛型方法来进一步证明这一点;这不会导致歧义相关的错误:

public class Bar : IBar
{
    public void Foo(int i)
    {

    }
    public void Foo<T>(T i)
    {
    }
}

至于实际解决问题,你可以这样做:

class FakeRepository : BaseRepository<User>, ICreatable<User>, IDeletable<User>, IRetrievable<int, User>
{
    public User Retrieve(int input)
    {
        return Retrieve<int>(input);
    }
}

这意味着FakeRespository同时具有Retrieve的通用和非通用版本,但最终所有调用仍然指向通用版本。

答案 1 :(得分:1)

编译器不知道TKey中的BaseRepository是什么,并且无法将其与IRetreivable相关联(请注意,泛型方法与非泛型方法不具有相同的签名) )。

我认为你需要更多这些内容,基类继承接口,并指定TKey

class FakeRepository : BaseRepository<int, User>
{
}

class BaseRepository<TKey, TPoco> : ICreatable<TPoco>, IDeletable<TPoco>, IRetrievable<TKey, TPoco> where TPoco : class,new()
{
    public virtual TPoco Create()
    {
        return new TPoco();
    }

    public virtual bool Delete(TPoco item)
    {
        return true;
    }

    public virtual TPoco Retrieve<TKey>(TKey input)
    {
        return null;
    }
}