从基类和派生类之外的方法返回任何派生类类型

时间:2021-02-03 17:41:42

标签: c# generics inheritance

我有三个类:BaseInfoCarInfoPetInfoBaseInfo 类是基类。 CarInfo 继承自 BaseInfo,并具有与汽车相关的属性。最后,PetInfo 也继承自 BaseInfo,并具有与宠物相关的属性:

public class BaseInfo
{
  [JsonIgnore]
  public int UserId { get; set; }
  [JsonIgnore]
  public string UserName { get; set; }
  [JsonIgnore]
  public DateTime RegistrationDate { get; set; } 
  [JsonIgnore]
  public DateTime LastUpdated { get; set; }    
}

public class CarInfo : BaseInfo
{
  [JsonPropertyName("car_make")] 
  public string Make { get; set; }
  [JsonPropertyName("car_model")]
  public string Model { get; set; }  
  …
}

public class PetInfo : BaseInfo
{
  [JsonPropertyName("pet_breed")] 
  public string Breed { get; set; }
  [JsonPropertyName("pet_weight")]
  public long Weight { get; set; } 
  …
}

然后我有一个服务,其目的是检索数据,并且在所讨论的方法的情况下,为特定用户创建汽车或宠物的列表:

public class SomeService : ISomeService
{
  public List<ANY_OF_THE_DERIVED_CLASSES> GetInfoList(string type, int userId)
  {
    List<ANY_OF_THE_DERIVED_CLASSES> infoList = new List<ANY_OF_THE_DERIVED_CLASSES>;
    switch (type)
    {
      case "car":
        infoList = GetCarList(userId);
        break;
      case "pet":
        infoList = GetPetList(userId);
        break;
      default:
        break;
    }
    return infoList;
  }

  private List<CarInfo> GetCarList(int userId)
  {
    var carList = new List<CarInfo>();

    //Logic that receives manipulates and serializes data against CarInfo and adds to list

    return carList;
  }

  private List<PetInfo> GetPetList(int userId)
  {
    var petList = new List<PetInfo>();

    //Logic that receives manipulates and serializes data against PetInfo and adds to list

    return petList;
  }
}

我还为 SomeService 创建了一个接口,其定义为:

 public interface ISomeService
 {
   List<ANY_OF_THE_DERIVED_CLASSES> GetInfoList(string type, int userId)
 }

是否可以让 GetInfoList 方法能够返回 List<CarInfo>List<PetInfo>

1 个答案:

答案 0 :(得分:2)

@Camilo-Terevinto 在评论中的指导完全正确,但如果您不熟悉泛型,可能会给您留下一些额外的问题。为了提供更全面的示例,您的代码(如您所提议的那样)应该如下所示:

ISomeService

public interface ISomeService 
{
    List<T> GetInfoList<T>(int userId) where T : BaseInfo;
}

SomeService

public class SomeService: ISomeService 
{
    public List<T> GetInfoList<T>(int userId) where T : BaseInfo 
    {
        List<T> infoList;
        switch (nameof(T)) 
        {
            case nameof(CarInfo):
                infoList = GetCarList(userId) as List<T>;
                break;
            case nameof(PetInfo):
                infoList = GetPetList(userId) as List<T>;
                break;
            default:
                infoList = new List<T>();
                break;
        }
        return infoList;
    }
    …
}

验证

您可以使用例如验证这是否有效以下测试类:

[TestClass]
public class SomeServiceTests 
{

    [TestMethod]
    public void GetInfoList_ShouldReturn_ListCarInfo() 
    {

      var service   = new SomeService();
      var list      = service.GetInfoList<CarInfo>(1);

      list.Add(new CarInfo());

      Assert.IsNotNull(list);
      Assert.AreEqual<int>(1, list.Count);

    }

注意事项

  • 您需要转换例如定义 List<CarInfo>List<T>infoList
  • 您不需要将 type 作为参数传入,因为您可以根据 T 类型参数(例如通过 nameof(T))来确定。
  • 您不需要预先初始化 infoList,因为在大多数情况下,它会立即被替换为例如的结果GetCarList()
  • 如果这是作为可重用库的一部分的公开可用接口,您应该更喜欢返回 Collection<T> 而不是 List<T> (source)。
  • 从设计的角度来看,我不喜欢硬编码的 switch 语句与初始化方法的 1:1 关系,但如果没有进一步深入了解您的业务需求,我将保持原样。

进一步优化

这与您最初的问题无关,但使用 C# 8.0 的模式匹配,您可以稍微清理一下 switch 语句:

public List<T> GetInfoList<T>(int userId) where T : BaseInfo => 
    nameof(T) switch 
    {
        nameof(CarInfo) => GetCarList(userId) as List<T>,
        nameof(PetInfo) => GetPetList(userId) as List<T>,
        _ => new List<T>()
    };
相关问题