值范围 - 如何确定没有if循环?

时间:2014-02-04 12:51:52

标签: c# .net range

我需要根据他的年龄找到某个人的sun-sign

例如,

Capricorn   December 22 – January 20
Aquarius    January 21 – February 18
Pisces      February 19 – March 19
Aries       March 20 – April 19
Taurus      April 20 – May 20
Gemini      May 21 – June 20
Cancer      June 21 – July 22
Leo         July 23 – August 22
Virgo       August 23 – September 22
Libra       September 23 – October 22
Scorpio     October 23 – November 21
Sagittarius November 22 – December 21

我写了这段代码,

 public enum Months
 {
     January = 1, February, March, April, May, June, July, August, September, October, November, December,
 }

var person = new Person(name:"mady", age:20, dateTime: new DateTime(2011,09,16));
if (person.DOB.Month == (int)Months.December) 
{
    if (person.DOB.Day >= 22)
        return "Capricorn";
    else
        return "Sagittarius";
} ...
....
....
....

IF语句会持续增长,如果明天名单增长,可能会成为一场噩梦。

有找到Sunsign的优雅方式吗? .NET中的EnumerableRange似乎不适合这种情况,或者这是编写代码的唯一方法吗?

6 个答案:

答案 0 :(得分:6)

创建一个StarSign类:

class StarSign
{
    public readonly string Name;
    public readonly DateTime StartDate;
    public readonly DateTime EndDate;

    public bool Contains(DateTime date);
}

将所有星标添加到StarSigns集合中。然后对于任何给定的DateTime日期(人的)做

foreach (var sign in StarSigns)
{
    if (sign.Contains(date))
    {
        Console.WriteLine("I am a: " + sign.Name);
            break;
    }
}

编辑,回复您的评论:

包含功能可以轻松比较日期,只需确保忽略年份:

public bool Contains(DateTime date)
{
    DateTime startNoYear = new DateTime(1904, StartDate.Month, StartDate.Day);
    DateTime endNoYear = new DateTime(1904, EndDate.Month, EndDate.Day);
    DateTime dateNoYear = new DateTime(1904, date.Month, date.Day);

    return dateNoYear >= startNoYear && dateNoYear <= endNoYear;
}

所以,是的,如果你有很多StarSigns,这将影响性能。 Normaly你只会有12个,而且既然你知道你正在处理一个封闭的套装,你就可以这样做。

在优化方面,您还需要存储startNoYear和endNoYear,而不是每次运行Contains时都计算它们。在构造函数中计算它们;我只是在方法中这样做,所以它更容易理解。更快的是直接处理DateTime属性并避免完全创建新的DateTime对象。就这个例子而言,我选择简化优于优化。

答案 1 :(得分:3)

请注意,您可以比较日期:

if (new DateTime(2012, 1, 1) < new DateTime(2012, 2, 1)) ...

因此,我建议

  • 您将DOB规范化为给定的闰年(例如1904年)
  • 然后只使用日期比较:

    DateTime dob = new DateTime(1904, person.DOB.Month, person.DOB.Day);
    
    if (dob >= new DateTime(1904, 12, 21))
        return "Aquarius";
    else if (dob >= new DateTime(1904, 11, 22))
        return "Sagittarius";
    else if (dob >= new DateTime(1904, 10, 23))
        return "Scorpio";
    ...
    else
        return "Aquarius";
    

一个明显的改进是创建一个List<Tuple<DateTime, String>>并迭代它。但是,由于日期不太可能在未来一百年内发生变化,因此在if条件下对其进行硬编码可能就足够了。

答案 2 :(得分:1)

您可以使用switch语句

switch (person.DOB.Month)
 {
   .....
   case 12:
      if (day >= 22) return "Capricorn"; else return "Sagittarius";
      break;
   .......

 }

答案 3 :(得分:0)

你可以建立一个太阳标志的小字典,它将太阳标志的名称作为关键字,并将它的时间跨度作为值。时间跨度是第一次到最后一次。 然后有一个标准时间函数来判断人员DOB是否在时间范围内。 Mayb你需要去除出生年份,但这应该很容易。

最后,您可以使用linq:

var sunsigns as Dictionary<string, TimeSpan>();
// adding sun-signs here

var sunsign = (from s in sunsigns where (methodToTellIfItsinRange(s)) select s).first();

答案 4 :(得分:0)

linq很好..只需使用你的清单...... 看看这个

    public class sing
{
    public string singName {
        get { return _singName; }
        set { _singName = value; }
    }

    private string _singName;
    public DateTime singStart {
        get { return _singStart; }
        set { _singStart = value; }
    }

    private DateTime _singStart;
    public DateTime singEnd {
        get { return _singEnd; }
        set { _singEnd = value; }
    }

    private DateTime _singEnd;


    public void findSing(System.DateTime usersDate)
    {
        List<sing> ListOfSings = new List<sing>();

        sing scorpio = new sing();
        System.DateTime startD = new System.DateTime(1910, 10, 23);
        System.DateTime endD = new System.DateTime(1910, 11, 21);
        scorpio.singName = "scorpio";
        scorpio.singStart = startD;
        scorpio.singEnd = endD;
        ListOfSings.Add(scorpio);
        //' ....etc all the others

        dynamic hismonth = usersDate.Month;
        dynamic hisDay = usersDate.Day;

        System.DateTime fixedDate = new System.DateTime(1910, hismonth, hisDay);

        dynamic q = (from i in ListOfSings where i.singStart >= fixedDate && i.singEnd <= fixedDatei).ToList;

        MessageBox.Show("your sing is: " + q.FirstOrDefault.singName);

    }
}

答案 5 :(得分:0)

DavidBožjak的答案是一个很好的选择。

我认为课程可以是抽象的,并为每个标志实施。 此外,StartDate,EndDate和日期传递参数需要忽略年份。 我这样做了:

public abstract class StarSign
{
    public readonly string Name;
    public readonly DateTime StartDate;
    public readonly DateTime EndDate;

    protected StarSign(string name, DateTime startDate, DateTime endDate)
    {
        Name = name;
        StartDate = startDate;
        EndDate = endDate;
    }

    public virtual bool Contains(DateTime date)
    {
        date = new DateTime(1, date.Month, date.Year);
        return date >= StartDate && date <= EndDate;
    }
}

public class AquariusStarSign : StarSign
{
    public AquariusStarSign()
        : base("Aquarius", new DateTime(1, 1, 21), new DateTime(1, 2, 18))
    {
    }
}

public class CapricornStarSign : StarSign
{
    public CapricornStarSign()
        : base("Capricorn", new DateTime(1, 12, 21), new DateTime(1, 1, 20))
    {
    }

    public override bool Contains(DateTime date)
    {
        if (date.Month == StartDate.Month)
            return date.Day >= StartDate.Day;

        if (date.Month == EndDate.Month)
            return date.Day <= EndDate.Day;

        return false;
    }
}