【问题标题】:C# - Nestable, bounded arraysC# - 可嵌套的有界数组
【发布时间】:2013-10-27 04:33:37
【问题描述】:

我正在将一个用 Pascal 编写的游戏(以 16 位编译)移植到 C#(因此它将在比 XP 更新的机器上运行)。根据我收集到的信息,在 Pascal 中,可以通过如下语法在单元/程序的类型部分中键入定义:

type
    BaseArrayPtr = ^BaseArray;
    BaseArray = array [1 .. 5, 1 .. 5] of Integer;

    SubArray = array [0 .. 3] of BaseArray;

我还了解到,不幸的是,无法在 C# 中键入定义。但是,我正在尝试解决方法。到目前为止,这就是我所拥有的:

BoundedArray.cs:

using System;
using System.Collections;

namespace test
{
    abstract class BoundedArray<T>
    {
        public BoundedArray()
        {
            m_data = null;
        }

        public T this[params int[] index]
        {
            get
            {
                if (index.Length != m_data.Rank)
                    throw new IndexOutOfRangeException();

                return (T) m_data.GetValue(index);
            }
            set
            {
                if (index.Length != m_data.Rank)
                    throw new IndexOutOfRangeException();

                m_data.SetValue(value, index);
            }
        }

        protected void SetAttributes(int[] lowerBounds, int[] lengths)
        {
            if (lengths.Length != lowerBounds.Length)
                throw new ArgumentException();

            m_lowerBounds = lowerBounds;
            m_lengths = lengths;

            m_data = Array.CreateInstance(typeof(T), m_lengths, m_lowerBounds);
            m_data.Initialize(); // Should (but doesn't) initialize every element in m_data
        }

        Array m_data;
        int[] m_lengths;
        int[] m_lowerBounds;
    }
}

test.cs:

using System;

namespace test
{
    class Program
    {
        public static int[] ints(params int[] values)
        {
            return values;
        }

        class BaseArray : BoundedArray<int>
        {
            public BaseArray()
            {
                SetAttributes(ints(2, 2), ints(1, 2));
            }
        }

        class SubArray : BoundedArray<BaseArray>
        {
            public SubArray()
            {
                SetAttributes(ints(4), ints(2));
            }
        }

        static void Main(string[] args)
        {
            SubArray subArray = new SubArray();

            Console.Read();
        }
    }
}

我检查了baseArraym_data 的默认值为零,因为它们是ints。但是,在subArray 中,m_data 的默认值为 null - subArray 中数组内的 BaseArray 实例由于某种原因尚未初始化。如何让默认构造函数运行?

编辑:目前真正的问题是为什么m_data.Initialize(); 方法中的SetAttributes 不初始化m_data 中的所有元素? The documentation on MSDN 似乎表明它应该...

编辑: 所以我认为问题在于System.Array.Initialize 仅适用于值类型。由于类是 C# 中的引用类型,System.Array.Initialize 不做任何事情。所以我必须找到一种方法来初始化一个可变维度、长度和下界的引用类型数组。

【问题讨论】:

  • 您尝试了什么?找到解决方案了吗
  • 不,我还没有找到解决方案。 System.Array.Initialize 的文档似乎表明它应该完全满足我的需要,但正如你所见,我把它放在那里,但它什么也没做......

标签: c# typedef pascal


【解决方案1】:

我做了一些更改,当您想要创建 SubArray 的实例时,您应该将 BaseArray 作为要初始化的数据源传递。

据我了解,您希望将值从 BaseArray 设置为 SubArray

这是我的工作:

BoundedArray.cs

 abstract class BoundedArray<T>
{
    public BoundedArray()
    {
        m_data = null;
    }

    public int[] Lengths;
    public int[] LowerBounds;

    public void CreateInstance()
    {
        if (Lengths.Length != LowerBounds.Length)
            throw new Exception("Incorrect number of lengths or lower bounds.");

        m_data = Array.CreateInstance(typeof(T), Lengths, LowerBounds);
    }
    public void CreateInstance(Array source)
    {
        if (Lengths.Length != LowerBounds.Length)
            throw new Exception("Incorrect number of lengths or lower bounds.");

        m_data = Array.CreateInstance(typeof(T), Lengths, LowerBounds);

        /************************************************************************/
        /*    Now you should find the value of BaseArray and set it to m_data                                                                     */
        /************************************************************************/


    }
    public T this[params int[] index]
    {
        get
        {
            if (index.Length != m_data.Rank)
                throw new IndexOutOfRangeException();

            return (T)m_data.GetValue(index);
        }
        set
        {
            if (index.Length != m_data.Rank)
                throw new IndexOutOfRangeException();

            m_data.SetValue(value, index);
        }
    }
    public Array GetData()
    {
        return m_data;
    }
    Array m_data;
}

Test.cs

class Program
{
    public static int[] ints(params int[] values)
    {
        return values;
    }

    class BaseArray : BoundedArray<int>
    {
        public BaseArray()
        {
            Lengths = ints(1, 2);
            LowerBounds = ints(2, 2);

            CreateInstance();
        }
    }

    class SubArray : BoundedArray<BaseArray>
    {
        public SubArray(BaseArray arr)
        {
            Lengths = ints(2);
            LowerBounds = ints(4);

            CreateInstance(arr.GetData());
        }
    }

    static void Main(string[] args)
    {
        BaseArray baseArray = new BaseArray();
        SubArray subArray = new SubArray(baseArray);

        Console.Read();
    }
}

【讨论】:

  • 我没有尝试将数据分配给BaseArray 中的任何数据 - 我只是尝试在SubArray 中实例化m_data 的每个元素。
  • 你想如何实例化SubArray 你想把它的值设置为零
  • 不一定。我想在数组中发生的任何事情上运行默认构造函数(在这种情况下,是 BaseArray 的元素)
  • 哦,在Array.CreateInstance 之后你应该做一些代码,其中每个元素的默认构造函数都适用。
  • initialize() 函数不起作用,因为我认为您还没有为它声明一个!
【解决方案2】:

您有一个单维数组SubArray,其中包含BaseArray 对象,它们是整数的二维数组。代替 Pascal type,您可以定义一个自定义 C# 类,该类将覆盖 indexer operator 以提供完全相同的行为。

已编辑 所以,在 Pascal 中你有这个:

type
    BaseArrayPtr = ^BaseArray;
    BaseArray = array [1 .. 5, 1 .. 5] of Integer;

    SubArray = array [0 .. 3] of BaseArray;

也许我误解了这个问题,但在 C# 中,下面的内容不完全相同吗?

public class BaseArray
{
    int[,] m_array = new int[5, 5];

    static void CheckBounds(int x, int y)
    {
        if (x < 1 || x > 5 || y < 1 || y > 5)
            throw new IndexOutOfRangeException();
    }

    public int this[int x, int y]
    {
        get 
        {
            CheckBounds(x, y);
            return m_array[x-1, y-1]; 
        }
        set 
        {
            CheckBounds(x, y);
            m_array[x-1, y-1] = value; 
        }
    }
}

public class SubArray
{
    BaseArray[] m_array = new BaseArray[4];

    public BaseArray this[int x]
    {
        get { return m_array[x]; }
        set { m_array[x] = value; }
    }
}

【讨论】:

  • 我知道。这就是我目前正在做的事情。问题是SubArray 的元素没有被实例化。
  • @BrianGradin,我更新了我的答案。我误解你的问题了吗?
  • @BrianGradin,我想我现在明白你的意思了:在泛型中使用int 作为参数是impossible。在这种情况下,我的回答不适用。
  • 浏览你的代码,看起来它可能会做我想做的事情,但我宁愿将BaseArraySubArray 都放在同一个类 - BoundedArray 上。这样一来,BaseArraySubArray 的类定义就更加简洁,正如您在编辑后的原始问题中的 test.cs 中看到的那样
【解决方案3】:

我已经回答过我自己的问题一次,但我想出了一个更好更好地实现我的答案。

以下是此解决方案的组成部分:

  1. SetAttributes 必须在基于 BoundedArray 的类的默认构造函数中运行一次
  2. SetAttributes 期间,我收集了当前BoundedArray 子类中所有索引的锯齿状二维数组
  3. 我通过调用 Activator.CreateInstance 并为每个索引分配一个来创建模板类型的实例

其他注意事项:

  • 设置属性现在采用可变长度数组int[]s,而不是两个int[]s。以前,它采用了下限和长度,但我意识到只采用 int[]s 下限和上限更有意义,然后使用 LINQ 查询来检查是否没有任何不是对
  • 我创建了一个名为IntArray 的静态类,SetAttributestest.cs 广泛使用了该类
  • 我尝试抛出尽可能多的有用错误,因为我最终可能会大量使用此代码
  • 我感觉Combinations(int[][] list1, int[] list2) 可能是我的解决方案的最大改进之处。我愿意接受有关如何改进所有代码的建议

所以,事不宜迟,我的完整解决方案:

BoundedArray.cs

using System;
using System.Linq;
using System.Collections.Generic;

namespace test
{
    static class IntArray
    {
        public static int[] FromValues(params int[] values)
        {
            return values;
        }

        public static int[] Sequence(int from, int length)
        {
            if (from < 0 || length < 1)
                throw new ArgumentException();

            return Enumerable.Range(from, length).ToArray();
        }

        public static int[][] Combinations(int[] list1, int[] list2)
        {
            return Combinations(list1.Select(i => new int[] { i }).ToArray(), list2);
        }

        public static int[][] Combinations(int[][] list1, int[] list2)
        {
            List<List<int>> result = new List<List<int>>();

            for (int i = 0; i < list1.Length; i++)
            {
                for (int j = 0; j < list2.Length; j++)
                    result.Add(((int[]) list1.GetValue(i)).Concat(new int[] { list2[j] }).ToList());
            }

            return result.Select(i => i.ToArray()).ToArray();
        }
    }

    abstract class BoundedArray<T>
    {
        public BoundedArray()
        {
            m_data = null;
        }

        public Array Value
        {
            get { return m_data; }
        }

        public T this[params int[] index]
        {
            get
            {
                if (index.Length != m_data.Rank)
                    throw new IndexOutOfRangeException();

                return (T) m_data.GetValue(index);
            }
            set
            {
                if (index.Length != m_data.Rank)
                    throw new IndexOutOfRangeException();

                m_data.SetValue(value, index);
            }
        }

        protected void SetAttributes(params int[][] values)
        {
            // Make sure all of the values are pairs
            if (values.Where(i => i.Length != 2).ToArray().Length > 0)
                throw new ArgumentException("Input arrays must be of length 2.");

            int[] lowerBounds = values.Select(i => i[0]).ToArray();
            int[] lengths = values.Select(i => i[1] - i[0] + 1).ToArray();

            m_data = Array.CreateInstance(typeof(T), lengths, lowerBounds);

            int[][] indices = (lowerBounds.Length != 1) ?
                IntArray.Combinations(IntArray.Sequence(lowerBounds[0], lengths[0]), IntArray.Sequence(lowerBounds[1], lengths[1]))
                : IntArray.Sequence(lowerBounds[0], lengths[0]).Select(i => new int[] { i }).ToArray();

            for (int i = 2; i < lowerBounds.Length; i++)
                indices = IntArray.Combinations(indices, IntArray.Sequence(lowerBounds[i], lengths[i]));

            for (int i = 0; i < indices.Length; i++)
                m_data.SetValue(Activator.CreateInstance(typeof(T)), indices[i]);
        }

        Array m_data;
    }
}

test.cs

using System;

namespace test
{
    class Program
    {
        // *** Examples of what you can do with BoundedArray ***

        // Multi-dimensional, bounded base array
        class BaseArray : BoundedArray<int>
        {
            public BaseArray()
            {
                SetAttributes(IntArray.FromValues(2, 3), IntArray.FromValues(2, 4));
            }
        }

        // One-dimensional, bounded subclass array
        class SubArray : BoundedArray<BaseArray>
        {
            public SubArray()
            {
                SetAttributes(IntArray.FromValues(4, 6));
            }
        }

        static void Main(string[] args)
        {
            // Initializations used for testing purposes
            BaseArray baseArray = new BaseArray();
            SubArray subArray = new SubArray();

            // Example of assignment
            baseArray[3, 4] = 3;
            subArray[4][2, 3] = 4;

            subArray[4][2] = 3; // Weakness: compiles, but causes IndexOutOfRangeException

            Console.Read();
        }
    }
}

想法?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多