powered by simpleCommunicator - 2.0.56     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Многомерный массив в одномерный и обратно - расширяющий метод?
5 сообщений из 5, страница 1 из 1
Многомерный массив в одномерный и обратно - расширяющий метод?
    #38576389
user7320
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если бы Array был дженериком, можно бы было сделать аналог такого

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
public static class ArrayExtensions
{
    public static T[] ToOneDimensionalArray<T>(this T[,] twoDimensionalArray)
    {
        int width = twoDimensionalArray.GetLength(0);
        int height = twoDimensionalArray.GetLength(1);
        T[] oneDimensionalArray = new T[width * height];

        int index = 0;
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                oneDimensionalArray[index] = twoDimensionalArray[x, y];
                index++;
            }
        }
        return oneDimensionalArray;
    }

    public static T[,] ToTwoDimensionalArray<T>(this T[] oneDimensionalArray)
    {

        int sqrt = (int)Math.Sqrt(oneDimensionalArray.Length);
        T[,] twoDimensionalArray = new T[sqrt, sqrt];

        int index = 0;
        for (int y = 0; y < sqrt; y++)
        {
            for (int x = 0; x < sqrt; x++)
            {
                twoDimensionalArray[x, y] = oneDimensionalArray[index];
                index++;
            }
        }
        return twoDimensionalArray;
    }
}



Только вместо T[,] на входе первого метода поставить Array и соответственно поменять типы во втором методе. Ну и поиграться с Rank внутри методов.

Но поскольку Array не дженерик , то облом.

У кого какие соображения по поводу того, как решить задачу?

Практическое применение - сериализация. Проблема с ней, например, такая .
...
Рейтинг: 0 / 0
Многомерный массив в одномерный и обратно - расширяющий метод?
    #38576591
Фотография pation
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
user7320,

вот несколько соображений:

-для копирования массивов используй Array.Copy

-сериализацию можно например выполнить BinaryFormatter-ом
...
Рейтинг: 0 / 0
Многомерный массив в одномерный и обратно - расширяющий метод?
    #38576895
user7320
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pationuser7320,

вот несколько соображений:

-для копирования массивов используй Array.Copy

-сериализацию можно например выполнить BinaryFormatter-ом
Спасибо, конечно... но, например, для XML-сериализации нужен именно одномерный массив. А копирование тут не причём - я просто перестраиваю порядок тех же самых элементов.
...
Рейтинг: 0 / 0
Многомерный массив в одномерный и обратно - расширяющий метод?
    #38579062
bazile
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
user7320, то что тип Array не является generic не мешает написать обобщенное решение для конвертации из многомерного массива в одномерный. Хотя, конечно, если бы массивы были бы generic-ами, это было бы проще.

Для обеспечения правильной конвертации многомерного массива в одномерный и обратно требуется сохранить не только данные, но и размерности массива. Приведенный тобой код может работать только с квадратными массивами т.к. функция ToTwoDimensionalArray использует Math.Sqrt(array.Length). Я бы ввел специальный тип для хранения "плоского" массива. Например, так:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
public class FlatArray<T>
{
	public int[] Dimensions;
	
	[XmlArrayItem("i")]
	public T[] Items;
}

public static class ArrayExtensions
{
	public static FlatArray<T> ToFlatArray<T>(this Array srcArray)
	{
		if (srcArray == null) return null;
		
		FlatArray<T> flatArr = new FlatArray<T>();
		
		flatArr.Dimensions = new int[srcArray.Rank];
		for (int i=0; i<srcArray.Rank; i++)
		{
			flatArr.Dimensions[i] = srcArray.GetLength(i);
		}
		
		if (srcArray.Rank == 1)
		{
			flatArr.Items = (T[])srcArray;
		}
		else
		{
			flatArr.Items = typeof(T).IsValueType ? FlattenValueTypeArray<T>(srcArray) : FlattenReferenceTypeArray<T>(srcArray);
		}
		
		return flatArr;
	}
	
	public static Array ToArray<T>(this FlatArray<T> flatArray)
	{
		if (flatArray == null) return null;
		
		Array arr = Array.CreateInstance(typeof(T), flatArray.Dimensions);
		if (typeof(T).IsValueType)
		{
			int byteLength = Buffer.ByteLength(flatArray.Items);
			Buffer.BlockCopy(flatArray.Items, 0, arr, 0, byteLength);
		}
		else
		{
			IEnumerator<int[]> indices = GetIndices(flatArray.Dimensions).GetEnumerator();
			foreach (T item in flatArray.Items)
			{
				indices.MoveNext();
				arr.SetValue(item, indices.Current);
			}
		}
		
		return arr;
	}
	
	private static T[] FlattenValueTypeArray<T>(Array srcArray)
	{
		var arr = new T[srcArray.Length];
		int byteLength = Buffer.ByteLength(srcArray);
		Buffer.BlockCopy(srcArray, 0, arr, 0, byteLength);
		return arr;
	}

	private static T[] FlattenReferenceTypeArray<T>(Array srcArray)
	{
        	T[] arr = new T[srcArray.Length];
		
		int index = 0;
		foreach (T item in srcArray)
		{
			arr[index] = item;
			index++;
		}
		
        	return arr;
	}
	
	private static IEnumerable<int[]> GetIndices(int[] dimensions)
	{
		long itemCount = 1;
		foreach (int dim in dimensions) itemCount *= dim;
		
		int[] indices = new int[dimensions.Length];
		
		int lastDim = dimensions.Length - 1;
		for (long i=0; i<itemCount; i++)
		{
			yield return indices;
			
			indices[lastDim]++;
			if (indices[lastDim] == dimensions[lastDim])
			{
				indices[lastDim] = 0;
				for (int nextDim = lastDim-1; nextDim>=0; nextDim--)
				{
					indices[nextDim]++;
					if (indices[nextDim] < dimensions[nextDim]) break;
					indices[nextDim] = 0;
				}
			}
			
		}
	}
}


Пример использования:
Код: c#
1.
2.
3.
4.
5.
6.
7.
int[,] arr1 = new int[2,3] {{1,2,3}, {4,5,6}};
FlatArray<int> flat1 = arr1.ToFlatArray<int>();
int[,] arr1copy = (int[,])flat1copy.ToArray();

string[,] arr2 = new string[2,3] {{"1","2","3"}, {"4","5","6"}};
FlatArray<string> flat2 = arr2.ToFlatArray<string>();
string[,] arr2copy = (string[,])flat2copy.ToArray();


Имея тип FlatArray<int> мы можем сериализовать/десериализовывать его вместо многомерного массива. Из-за того что Array не generic тип приходится явно указывать тип элемента массива и выполнять явное приведение типа после вызова метода ToArray(). Это мелочь с которой можно смириться. Или можно вместо метода ToFlatArray<T> работающий с типом Array поставить реализации под кокретные размерности, что избавит от необходимости указывать тип-аргумент.
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
public static FlatArray<T> ToFlatArray<T>(this T[,] srcArray)
{
	if (srcArray == null) return null;
	
	FlatArray<T> flatArr = new FlatArray<T>();
	flatArr.Dimensions = new int[] { srcArray.GetLength(0), srcArray.GetLength(1) };
	flatArr.Items = typeof(T).IsValueType ? FlattenValueTypeArray<T>(srcArray) : FlattenReferenceTypeArray<T>(srcArray);
	
	return flatArr;
}

public static FlatArray<T> ToFlatArray<T>(this T[,,] srcArray)
{
	if (srcArray == null) return null;
	
	FlatArray<T> flatArr = new FlatArray<T>();
	flatArr.Dimensions = new int[] { srcArray.GetLength(0), srcArray.GetLength(1), srcArray.GetLength(2) };
	flatArr.Items = typeof(T).IsValueType ? FlattenValueTypeArray<T>(srcArray) : FlattenReferenceTypeArray<T>(srcArray);
	
	return flatArr;
}


Приведенное решение должно работать с массивами любого размера при условии что это не jagged массив. Самой интересной частью оказалась реализация восстановления элементов многомерного массива. Для value типов спасает метод Buffer.BlockCopy(), но он не работает для ссылочных типов. Пришлось делать копирование поэлементно, что наверняка замедлит десереализацию больших массивов. Было бы неплохо улучшить, но пока нет идей как.
...
Рейтинг: 0 / 0
Многомерный массив в одномерный и обратно - расширяющий метод?
    #38579063
bazile
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pation-для копирования массивов используй Array.Copy
Array.Copy() работает с массивами одинаковой размерности, а здесь размерности разные.
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Многомерный массив в одномерный и обратно - расширяющий метод?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]