【问题标题】:Can you explain this generics behavior and if I have a workaround?你能解释一下这种泛型行为吗?如果我有解决方法?
【发布时间】:2012-10-29 16:58:14
【问题描述】:

下面的示例程序:

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 使用什么参数约束似乎并不重要,我永远无法让方法调用落入基类。

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

想法?

【问题讨论】:

    标签: c# oop generics implementation


    【解决方案1】:

    该代码无法编译的原因与这个更简单的示例无法编译的原因相同:

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

    这些方法根本没有相同的签名。是的,您可以调用someBar.Foo(5),它会将T 解析为int,但事实仍然是Bar 中的Foo 仍然没有与实际采用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 的通用版本和非通用版本,但最终所有调用仍然指向通用版本。

    【讨论】:

      【解决方案2】:

      编译器不知道BaseRepository 中的TKey 是什么,也无法将其与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;
          }
      }
      

      【讨论】:

      • 要补充一点,不幸的是我没有在原始示例中包含这个,我的 FakeRepository 可能定义了多个 IRetrievable 接口,它们都具有相同的 TPoc​​o,但 TKey 不同。
      猜你喜欢
      • 2010-09-15
      • 1970-01-01
      • 1970-01-01
      • 2013-06-20
      • 2020-07-08
      • 1970-01-01
      • 1970-01-01
      • 2019-03-09
      • 1970-01-01
      相关资源
      最近更新 更多