【问题标题】:How do I create a generic List using abstract class?如何使用抽象类创建通用列表?
【发布时间】:2019-08-01 19:36:07
【问题描述】:

我有一个 Json 类“GetAllDevices()”。我的 JSON 响应由一个对象数组/列表组成,其中每个对象都具有以下常见属性。

public class GetAllDevices
{
    [JsonProperty("_id")]
    public string Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("type")]
    public string Type { get; set; }

    [JsonProperty("actions")]
    public Action[] Actions { get; set; }

    public class Action
    {
        public string _id { get; set; }
        public Action_Def action_def { get; set; }
    }

    public class Action_Def
    {
        public string _id { get; set; }
        public string name { get; set; }
    }
}

我想创建 2 个通用列表,其中包含基于其“类型”的所有上述属性。 lstfoo1 列表包含 type="foo1" 的所有属性(_id、名称类型和操作)。同样,lstfoo2 是一个 List,其中包含上述属性,其中 type="foo2"。

到目前为止我做了什么:

string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);


public class AllFoo1: GetAllDevices
{
}

public class AllFoo2: GetAllDevices
{
}

public abstract class HomeDevices<T>
{
    public string type { get; set; }
    public string _id { get; set; }
    public List<AllFoo1> lstfoo1{ get; set; }
    public List<AllFoo2> lstfoo2{ get; set; }
    public abstract List<T> GetDeviceData(string jsonResult);
}

public class Foo1: HomeDevices<AllFoo1>
{
    public Foo1()
    {
        type = "foo1";
    }

    public override List<AllFoo1> GetDeviceData(string jsonResult)
    {
        var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
        var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
        return lst1;
    }
}

public class Foo2: HomeDevices<AllFoo2>
{
    public Foo2()
    {
        type = "foo2";
    }

    public override List<AllFoo2> GetDeviceData(string jsonResult)
    {
        var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
        var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
        return lst1;
    }
}

我的问题是,有没有更简单的方法可以使用抽象类来做到这一点?我可以直接将我的“GetAllDevices”类转换为抽象类并继承它并反序列化并创建一个通用列表吗?

【问题讨论】:

  • GetDeviceData 的实现看起来完全一样。你不能只在基类上实现它并在调用DeserializeObject时使用T作为参数吗?
  • 是的,您可以创建一个 List 并将任一类型的对象放入其中,但听起来您正在尝试将对象的属性而不是对象本身放入列表中,是吗正确的?在这种情况下,您可以只使用 List。您始终可以使用getType 来区分属性类型。
  • 我猜他想在列表中强制执行类型
  • @ĴošħWilliard 我希望能够访问每个对象的所有属性。我对使用抽象类非常陌生。我看过各种例子,但不知道如何将我的放在其中。你能给我一个链接吗?
  • @JuanR 你能告诉我怎么做或者指向一个链接吗?

标签: c# json abstract-class generic-list


【解决方案1】:

如果我正确理解您的问题,这应该会有所帮助。让我知道您是否有任何问题或它不能按您的需要工作。我很快就把它组装好了,没有经过测试。

Type 属性的定义方式可以改进,但我保留了它。

public class MyApplication
{
    public void DoWork()
    {
        string json = getJSON();
        DeviceTypeOne foo1 = new DeviceTypeOne();
        DeviceTypeTwo foo2 = new DeviceTypeTwo();
        IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
        IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
    }        
}

// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
    public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
    {
        IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
        IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
        return resultList.ToList();
    }
}

// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
    protected DeviceBase(string type)
    {
        if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}

        Type = type; // type's value can only be set by classes that inherit and must be set at construction time
    }
    [JsonProperty("_id")]
    public string Id { get; set; }
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("type")]
    public string Type { get; private set;} 
    [JsonProperty("actions")]
    public DeviceAction[] Actions { get; set; }
}

public class DeviceTypeOne : DeviceBase
{
    public DeviceTypeOne() : base(nameof(DeviceTypeOne))
    {
    }
}

public class DeviceTypeTwo : DeviceBase
{
    public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
    {
    }
}

// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
    string Id { get; set; }
    string Name { get; set; }
    string Type { get; }
    DeviceAction[] Actions { get; set; }
}

// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
    public string Id { get; set; }
    public DeviceActionDefinition DeviceActionDefinition { get; set; }
}

// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
    public string Id { get; set; }
    public string Name { get; set; }
}

【讨论】:

  • 你能告诉我为什么我们使用抽象类和接口吗?此外,我的 json 类是一个设备列表/数组,并且具有比 id、name 和操作更多的属性。但重要的是要知道,所有这些属性对所有设备都是通用的。所以我的想法是将整个“GetAllDevices”转换为一个抽象类。
  • 我保留了抽象基类,因为您已经有了一个,它允许在构造时控制类型。您可以放弃 IDeviceData 接口,但我添加它是因为您可能需要持久化数据,并且该接口可以在应用层之间传递数据时提供帮助。
  • 你到底在哪里使用DeviceExtension的方法?
  • 我的意思是,你到底在哪里称呼它?如果我问的是基本问题,我很抱歉,我还在学习。
  • 我在这条线上调用它... IList foo1Results = foo1.GetDeviceData(json);... 我将在我的帖子中添加一些 cmets。扩展方法:docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
【解决方案2】:

将方法GetDeviceData()的实现移动到基类应该足够简单。

为此,您需要在T 上添加一个约束,以便编译器了解更多关于基本类型的信息。您还需要实现一个构造函数来填充您使用的具体类型的type 字符串。这是确保在相关方法中用于比较时始终填充该值的必要措施:

public abstract class HomeDevices<T> where T: GetAllDevices
{
    public HomeDevices(string concreteType)
    {
        type = concreteType;
    }

    public string type { get; set; }
    public string _id { get; set; }
    public List<AllFoo1> lstfoo1 { get; set; }
    public List<AllFoo2> lstfoo2 { get; set; }

    //This method is now generic and works for both.
    public List<T> GetDeviceData(string jsonResult)
    {
        var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
        var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
        return lst1;
    }
}

希望对你有帮助。

【讨论】:

  • 这确实有帮助,谢谢!另外,你能告诉我如果我不打算创建 2 个不同的列表,是否需要使用抽象类?
  • @V_stack:抽象类应该由具体类共享。这真的取决于你的设计。我通常从接口和具体类开始,然后当我意识到我有更专业的事物版本时引入抽象(基)类。祝你好运!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-01
  • 2021-10-12
  • 2021-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多