【问题标题】:C# - How can I make one array of different classes that share common properties?C# - 如何创建一组共享公共属性的不同类?
【发布时间】:2017-06-30 23:36:20
【问题描述】:

我有一个名为 PersonActivity 的 C# 类。它包含一个唯一的 Id (int)、一个 PersonId (int) 和一个 ActivityType (string)。

我有另一个继承自 PersonActivity 的类 HighlightedActivity。我需要做的是创建一个名为“activities”的数组 HighlightedActivity[],我已经完成了。我的问题是如何制作这个数组,但具有基于 ActivityType 的某些附加属性?

我的意图是遍历 HighlightedActivity 类型的“活动”,然后设置基于 ActivityType 的附加属性。活动类型是诸如具有 ISBN 号的书籍之类的东西,而期刊有一个卷和期号,而赠款有一个金额。它们都有来自 PersonActivity 类的 Id、PersonId 和 ActivityType。我不确定 HighlightedActivity 类应该是什么样子,以便我可以将所有这些项目放在同一个列表中。

数组“活动”应如下所示:

[0] {Id = 2, PersonId = 5, ActivityType = "Book", ISBN = "13239382"}

[1] {Id = 3, PersonId = 5, ActivityType = "Journal", Volume = 5, Issue = 124}

[2] {Id = 4, PersonId = 5, ActivityType = "Journal", Volume = 8, Issue = 201}

[3] {Id = 5, PersonId = 5, ActivityType = "Journal", Volume = 8, Issue = 202}

[4] {Id = 6, PersonId = 5, ActivityType = "Grant", Amount = 444.00}

【问题讨论】:

    标签: c# .net arrays generics


    【解决方案1】:

    您可以创建基本活动类(如果您不打算在创建后更改它们,还可以考虑将活动设为只读值对象):

    public abstract class Activity
    {
        public int Id { get; set; }
        public int PersonId { get; set; }
        public string ActivityType { get; private set; } // consider to use enum
    
        public Activity(string activityType)
        {
            ActivityType = activityType;
        }
    }
    

    然后创建继承这个基类的自定义活动类:

    public class Book : Activity
    {
        public Book() : base("Book") { }
    
        public string ISBN { get; set; }
    }
    
    public class Journal : Activity
    {
        public Journal() : base("Journal") { }
    
        public int Volume { get; set; }
        public int Issue { get; set; }
    }
    

    并保留基本类型活动的列表:

    var activities = new List<Activity> {
       new Book { Id = 2, PersonId = 5, ISBN = "13239382" },
       new Journal { Id = 3, PersonId = 5, Volume = 5, Issue = 124 },
       new Journal { Id = 4, PersonId = 5, Volume = 8, Issue = 201 },
       new Journal { Id = 5, PersonId = 5, Volume = 8, Issue = 202 }
       // etc
    };
    

    然后您可以将每个活动转换为适当的类型并检索其属性(使用 C# 7 模式匹配的示例):

    foreach(var activity in activities)
    {
        switch(activity)
        {
           case Book book:
               /* use book here */
               break;
           case Journal journal:
               /* use journal here */ 
               break;
           default:
               /* handle unknown activity */
               break;
        }
    }
    

    您还可以按活动类型切换并将活动转换为适当的类。

    【讨论】:

    • 谢谢!我没有意识到我可以像这样声明一个 Activity 数组。这看起来像我需要的:)
    【解决方案2】:

    您可以创建一个基本活动类并从中继承您的其他对象。基类将具有可用于跟踪活动类型的活动类型。

    将其定义为枚举:

    public enum ActivityType
    {
        Book,
        Journal,
        Grant
    }
    

    您的基类将包含通用属性。它还将具有构造函数来按活动类型和数组创建派生类。由于我们将有一个基于数组的构造函数,我们将验证数组长度并添加可用于将数组对象转换为正确类型的受保护成员。

    public abstract class Activity
    {
        public int Id { get; set; }
        public int PersonId { get; set; }
        public ActivityType ActivityType { get; private set; } // consider to use enum
    
        public Activity(ActivityType activityType)
        {
            ActivityType = activityType;
        }
        public Activity(object[] values)
        {
            if (values.Length < 4)
                throw new ArgumentException();
    
            this.Id = getInt(values[0]);
            this.PersonId = getInt(values[1]);
            this.ActivityType = getActivityType(values[2]);
        }
        protected int getInt(object value)
        {
            if (!(value is int))
            {
                throw new ArgumentException();
            }
            return (int)value;
        }
        protected string getString(object value)
        {
            if (!(value is string))
            {
                throw new ArgumentException();
            }
            return (string)value;
        }
        protected decimal getDecimal(object value)
        {
            if (!(value is decimal))
            {
                throw new ArgumentException();
            }
            return (decimal)value;
        }
        protected ActivityType getActivityType(object value)
        {
            ActivityType result;
            if(value is string)
            {
                if (!Enum.TryParse((string)value, out result))
                {
                    throw new ArgumentException();
                }
            }
            else if(value is ActivityType)
            {
                result = (ActivityType)value;
            }
            else
            {
                throw new ArgumentException();
            }
            return result;
        }
    }
    

    我们现在定义派生类,它们将调用基本构造函数来根据需要设置基本属性。对于数组构造函数,验证数组长度并使用基本方法将数组对象转换为正确的类型。

    public class Book : Activity
    {
        public Book() : base(ActivityType.Book) { }
        public Book(object[] values) : base(values)
        {
            if (values.Length != 4) throw new ArgumentException();
            this.ISBN = getString(values[3]);
        }
        public string ISBN { get; set; }
    }
    
    public class Journal : Activity
    {
        public Journal() : base(ActivityType.Journal) { }
        public Journal(object[] values) : base(values)
        {
            if (values.Length != 5) throw new ArgumentException();
            this.Volume = getInt(values[3]);
            this.Issue = getInt(values[4]);
        }
    
        public int Volume { get; set; }
        public int Issue { get; set; }
    }
    
    public class Grant : Activity
    {
        public Grant() : base(ActivityType.Grant) { }
        public Grant(object[] values) : base(values)
        {
            if (values.Length != 4) throw new ArgumentException();
            this.Volume = getDecimal(values[3]);
        }
        public decimal Volume { get; set; }
    
    }
    

    最后,我们将创建一个类,该类使用工厂模式从数组创建活动或从数组数组创建活动列表。工厂还将有一个方法来测试您在上面定义的数据。

    public class ActivityFactory
    {
        public static List<Activity> GetActivities(object[][] values)
        {
            List<Activity> result = new List<Activity>();
            for (int i = 0; i < values.Length; i++)
            {
                result.Add(MakeActivity(values[i]));
            }
            return result;
        }
        static Activity MakeActivity(object[] values)
        {
            Activity result = null;
            switch (values[2].ToString())
            {
                case "Book":
                    result = new Book(values);
                    break;
                case "Journal":
                    result = new Journal(values);
                    break;
                case "Grant":
                    result = new Grant(values);
                    break;
                default:
                    throw new ArgumentException("Invalid activity " + values[2].ToString());
    
            }
            return result;
        }
        public static void RunTest()
        {
            object[][] values = new object[][] {
               new object[] { 2, 5, "Book", "13239382"},
               new object[] { 3, 5, "Journal", 5, 124},
               new object[] { 4, 5, "Journal", 8, 201},
               new object[] { 5, 5, "Journal", 8, 202},
               new object[] { 6, 5, "Grant", 444.00m}
           };
            List<Activity> activities = GetActivities(values);
        }
    }
    

    【讨论】:

    • 谢谢!它看起来像我需要的。在这里,我遍历它们并根据类型分配属性。我想我什至可以使用一些代码,因为我不会预先拥有每个 ActivityType 的自定义值。 dotnetfiddle.net/dXmwwK
    • 欢迎您。您的问题是 .net 中的常见用例,工厂模式是处理此类用例同时保持逻辑清晰的好方法。您可以将答案标记为已接受,以便阅读它的其他人知道它对您有帮助。
    猜你喜欢
    • 2014-10-16
    • 1970-01-01
    • 2014-04-21
    • 2014-07-16
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 2017-08-10
    相关资源
    最近更新 更多