【问题标题】:C# accessing anArraylist of floatsC# 访问浮点数的数组列表
【发布时间】:2021-05-31 22:27:47
【问题描述】:

我正在尝试解决一个小问题: 想象一下每月每天进行 1440 次温度测量。

"int Day[31]float Temp[1440]".

Microsoft 文档和我发现的所有其他内容都类似于:

float[][] jaggedArray =
{
    new float[] { 88.3F, 33.9F, 55.4F,  99.9F },
    new float[] { 1.1F, 3F, 5F,  9.9F },
    new float[] { 1F, 3F, 5F,  9F }
};

现在我可以访问任何元素了

float result = jaggedArray[2][1];

到目前为止一切顺利。

缺点是我必须先调整数组的大小和初始化,然后 数据集的大小可能不同。 (不是每个月都有 30 天)

所以我认为使用 ArrayList 的 Arraylist 会是一个好主意。

            ArrayList valueSet1= new ArrayList();
            ArrayList valueSet2 = new ArrayList();
            ArrayList Sets = new ArrayList();

                valueSet1.Add(1.0F);
                valueSet1.Add(2.0F);
                valueSet1.Add(3.0F);
                valueSet1.Add(4.0F);

                valueSet2.Add(5.0F);
                valueSet2.Add(6.0F);
                valueSet2.Add(7.0F);
                valueSet2.Add(8.0F);

                Sets.Add(valueSet1);
                Sets.Add(valueSet2);

这也有效,在监视窗口中它看起来与数组解决方案几乎相同,除了访问值不起作用:

float test= Sets[1][1]; //does not work

我还测试了一个 foreach 方法,但没有成功:

 foreach (var ValueSet in Sets)
            {
              // ValueSet exists here but:
               foreach (var item in ValueSet)  // does not recognize ValueSet 
                {
                    
                }
            }

我现在被难住了。

感谢您的帮助。

【问题讨论】:

  • ArrayList 类在 2002 年和 2003 年很有用。自 2005 年 .NET Framework 的泛型类功能发布以来,它已被废弃。如果你想要一个“有弹性”的数组花车,你真的想使用List<float>。如果你想要一个有弹性的浮动数组的弹性数组,请使用List<List<float>>。但是,这可能不是这个问题的答案。
  • 也许(简体)List<Dictionary<int, List<float>>>。在一年内,您有一个包含 12 个字典的列表,其中键是日期,值是您的浮点数列表。

标签: c# arraylist


【解决方案1】:

如果我要这样做,我会采取完全不同的策略。我会有一个代表一天测量值的类。然后,我将有一个代表一个月的每日测​​量值的类 - 它将包含每日测量实例的字典,按月中的某天索引。

所以,开始做一些家务。我喜欢象征性地工作,...

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

现在我可以使用Months.September 而不是9

现在我将创建一些东西来跟踪白天的温度测量。我将为每次测量添加时间戳(使用TimeSpan)。我将在Dictionary<TimeSpan, float> 中跟踪测量结果,并根据记录时间跟踪每个测量结果。首先是简单的部分:

public class DailyTemperatureMeasurements : IEnumerable<(TimeSpan time, float temperature)>, IEnumerable<float>
{
    private readonly Dictionary<TimeSpan, float> _measurements = new Dictionary<TimeSpan, float>();
    private IEnumerable<float> _enumerableImplementation;

    public void Add(TimeSpan time, float temperature)
    {
        _measurements.Add(time, temperature);
    }

    IEnumerator<float> IEnumerable<float>.GetEnumerator()
    {
        foreach (var measurement in _measurements)
        {
            yield return measurement.Value;
        }
    }

    public IEnumerator<(TimeSpan time, float temperature)> GetEnumerator()
    {
        foreach (var measurement in _measurements)
        {
            yield return (measurement.Key, measurement.Value);
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<float>) this).GetEnumerator();
    }
}

请注意,我通过两种方式创建了 Enumerable 类。您可以枚举它并获取当天的所有温度(没有时间戳),或者您可以获得表示时间戳/测量值对的 Tuples。我不使用其中任何一个,但您可能会觉得它很方便。

另外,因为类是可枚举的并且有一个 Add 方法,你可以使用Collection Initialization。同样,我不使用它,但你可以使用。

最难的部分是把价值观拿出来。我怀疑您可能希望使用插值来获得在特定时间对测量的最佳估计。我太懒了,所以我在要求的时间之前选择了一个。此外,虽然字典通常按添加顺序返回项目,但 Dictionary&lt;TKey, TValue&gt; 类明确表示枚举的顺序是未定义的(所以我通过所有值来解决这个问题)。您可能想看看另一种方法来做到这一点。

那么,让我们将这个函数添加到DailyTemperatureMeasurements 类...

public float GetMeasurementAt(TimeSpan time)
{
    if (!_measurements.Any())
    {
        throw new Exception("There are no measurements registered in this object");
    }

    if (_measurements.ContainsKey(time))
    {
        return _measurements[time];
    }

    //you may want to do interpolation, but I'm keeping it simple
    //find the last measurement before the requested time
    var lastClosestTime = default(TimeSpan);
    foreach (var measurement in _measurements)
    {
        var timeToMeasurement = time - measurement.Key;
        if (timeToMeasurement < time - lastClosestTime && timeToMeasurement > default(TimeSpan))
        {
            lastClosestTime = measurement.Key;
        }
    }

    if (lastClosestTime == default)
    {
        return 0.0f;
    }

    return _measurements[lastClosestTime];
}

现在我正在跟踪一天的所有测量结果。让我们跟踪他们一个月:

正如您所指出的,并非所有月份都有 31 天。它变得比这更复杂(二月)。我们不想要坏数据,所以我有这两个小私人工人,我将把它们添加到我的每月测量课程中:

 private static readonly Dictionary<Months, int> MonthLengths = new Dictionary<Months, int>
 {
     {Months.January, 31},
     {Months.February, 28},
     {Months.March, 31},
     {Months.April, 30},
     {Months.May, 31},
     {Months.June, 30},
     {Months.July, 31},
     {Months.August, 31},
     {Months.September, 30},
     {Months.October, 31},
     {Months.November, 30},
     {Months.December, 30},
 };

 private static int GetMonthLength(Months month, int year)
 {
     if (month != Months.February)
     {
         return MonthLengths[month];
     }
     //otherwise, month is February, so...
     if (year % 400 == 0)
     {
         return 29;
     }

     if (year % 100 == 0)
     {
         return 28;
     }

     if (year % 4 == 0)
     {
         return 29;
     }

     return 28;
 }

这样,如果有人尝试添加 2100 年 4 月 31 日或 2 月 29 日的数据,我可以抛出异常。

跟踪每月的测量结果实际上非常简单:

public class TemperatureMeasurements
{
    private readonly Dictionary<int, DailyTemperatureMeasurements> _dailyMeasurements = new Dictionary<int, DailyTemperatureMeasurements>();

    public TemperatureMeasurements(int year, Months month)
    {
        //you may want to sanity check the year value (no years less than now, no years greater than 2500) - maybe
        Year = year;
        Month = month;
    }

    public int Year { get; }
    public Months Month { get; }

    public void Add(int day, TimeSpan time, float measurement)
    {
        if (day < 1 || day > GetMonthLength(Month, Year))
        {
            throw new ArgumentOutOfRangeException(nameof (day), "Day value must be within range for month: {_month}");
        }

        if (!_dailyMeasurements.ContainsKey(day))
        {
            _dailyMeasurements.Add(day, new DailyTemperatureMeasurements());
        }

        _dailyMeasurements[day].Add(time, measurement);
    }

    public void Add(TimeSpan time, float measurement)
    {
        var now = DateTime.Now;
        var monthToday = (Months)now.Month;
        if (monthToday != Month)
        {
            throw new ArgumentException($"This overload of {nameof(Add)} can only be used with today's date is within the month associated with this {nameof(TemperatureMeasurements)} instance ");
        }

        var dayToday = now.Day;
        Add(dayToday, time, measurement);
    }

    public void Add(float measurement)
    {
        var now = DateTime.Now.TimeOfDay;
        Add(now, measurement);
    }

    public float GetMeasurementAt(int day, TimeSpan time)
    {
        if (!_dailyMeasurements.ContainsKey(day))
        {
            throw new ArgumentOutOfRangeException(nameof(day), "No measurements for {Month} {day} have been recorded");
        }

        return _dailyMeasurements[day].GetMeasurementAt(time);
    }

    //This is where that MonthLengths dictionary and GetMonthLengths function go
}

您需要提供其中一个将用于的月份和年份(以便可以正确计算一个月中的天数)。

数据保存在字典中,其中键是月份中的某天(1..[月份中的天数]),值是DailyTemperatureMeasurements(我们之前创建的类)实例。

您可以通过多种方式添加数据:

  • 您可以指定月份中的日期、时间和测量值
    void Add(int day, TimeSpan time, float measurement)
  • 您可以只指定时间和测量值(假设您的意思是今天)
    void Add(TimeSpan time, float measurement)
  • 您可以只指定测量值(假设您的意思是今天和现在的日期和时间) void Add(float measurement)

要获取值,请调用GetMeasurementAt,传递月份中的日期和时间。它将呼叫委托给请求日期的适当DailyTemperatureMeasurements

此代码经过了非常轻微的测试。你会想要更多地测试它。它应该使记录测量更容易。

【讨论】:

    【解决方案2】:

    正如其他人所提到的,现在有比 ArrayList 更好的选择。

    如果你仍然想继续使用它,你可能想要这样做:

    float x = (float)((ArrayList)Sets[1])[1];
    

    foreach (ArrayList list in Sets)
        foreach (float y in list)
            MessageBox.Show(y.ToString());
    

    ArrayList 内部有“对象”,您需要显式转换它们。

    【讨论】:

    • 致各位:非常感谢!!!我将不得不在接下来的几天或周末消化这个!
    猜你喜欢
    • 1970-01-01
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 1970-01-01
    • 2010-12-19
    • 2020-02-20
    • 2019-09-13
    相关资源
    最近更新 更多