在对象被其他对象引用后初始化对象
Initialize an object after it is referenced by an other
我有一个二维数组,它存储棋盘的字段..
private field[,] board = new field[boardsize,boardsize];
..现在当我初始化它们时,我想引用每个字段的邻居,这些字段部分尚未初始化。
目前使用这样的解决方法:
for(int x = 0; x < boardsize ; x++)
{
for(int y = 0; y < boardsize ; y++)
{
board[x,y] = new field();
}
}
for(int x = 0; x < boardsize ; x++)
{
for(int y = 0; y < boardsize ; y++)
{
board[x,y].setNeighbours(x, y, board);
}
}
这很好用,但我很感兴趣是否可以在初始化之前或初始化时设置邻居。
setNeighbour - 方法:
setNeighbour(int x, int y, field[,])
{
if(field[x-1,y] != null)
this.neighbour[0] = field[x-1,y];
if(field[x-1,y+1] != null)
this.neighbour[0] = field[x-1,y+1];
if(field[x,y+1] != null)
this.neighbour[0] = field[x,y+1];
if(field[x+1,y+1] != null)
this.neighbour[0] = field[x+1,y+1];
if(field[x+1,y] != null)
this.neighbour[0] = field[x+1,y];
if(field[x+1,y-1] != null)
this.neighbour[0] = field[x+1,y-1];
if(field[x,y-1] != null)
this.neighbour[0] = field[x,y-1];
if(field[x-1,y-1] != null)
this.neighbour[0] = field[x-1,y-1];
}
我认为不可能引用邻居,因为它们尚未创建。但是,您可以将板作为参数添加到字段的构造函数中,并在以后需要时使用 属性 查找邻居。
你可以这样做:
private field[,] board = new field[boardsize,boardsize];
for(int x = 0; x < boardsize ; x++)
{
for(int y = 0; y < boardsize ; y++)
{
if(board[x,y] == null)
board[x,y] = new field();
board[x,y].setNeighbours(x, y, board);
}
}
并以相同的方式在 setNeighbours()
方法中检查邻居的初始化:
private void setNeighbours(int x, int y, field[,] board)
{
//Initialize neighbour if not already initialized
if(board[x+1,y] == null)
board[x+1,y] = new field();
//DO SOMETHING...
}
这是一个经典问题...遗憾的是 C# 没有延续...它们可以通过使用 Thread
s 来模拟(非常缓慢)...是的,这太过分了,这只是一个编程练习:
public sealed class WaitForReady<T> : IDisposable
{
public readonly ManualResetEvent mre = new ManualResetEvent(false);
public T Value
{
get
{
mre.WaitOne();
return this.value;
}
set
{
this.value = value;
mre.Set();
}
}
private T value;
#region IDisposable Members
public void Dispose()
{
mre.Dispose();
}
#endregion
}
public class Field
{
public Field[] Neighbours;
public Field(WaitForReady<Field> me, WaitForReady<Field>[] neighbours)
{
me.Value = this;
Neighbours = new Field[neighbours.Length];
for (int i = 0; i < neighbours.Length; i++)
{
Neighbours[i] = neighbours[i] != null ? neighbours[i].Value : null;
}
}
}
public static void Main(string[] args)
{
int boardsize = 8;
Field[,] board = new Field[boardsize, boardsize];
WaitForReady<Field>[,] boardTemp = null;
try
{
boardTemp = new WaitForReady<Field>[boardsize, boardsize];
var threads = new Thread[boardsize * boardsize];
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
boardTemp[i, j] = new WaitForReady<Field>();
}
}
int k = 0;
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
var neighbours = new WaitForReady<Field>[8];
neighbours[0] = i > 0 && j > 0 ? boardTemp[i - 1, j - 1] : null;
neighbours[1] = i > 0 ? boardTemp[i - 1, j] : null;
neighbours[2] = i > 0 && j + 1 < boardsize ? boardTemp[i - 1, j + 1] : null;
neighbours[3] = j > 0 ? boardTemp[i, j - 1] : null;
neighbours[4] = j + 1 < boardsize ? boardTemp[i, j + 1] : null;
neighbours[5] = i + 1 < boardsize && j > 0 ? boardTemp[i + 1, j - 1] : null;
neighbours[6] = i + 1 < boardsize ? boardTemp[i + 1, j] : null;
neighbours[7] = i + 1 < boardsize && j + 1 < boardsize ? boardTemp[i + 1, j + 1] : null;
int i1 = i, j1 = j;
threads[k] = new Thread(() => board[i1, j1] = new Field(boardTemp[i1, j1], neighbours));
threads[k].Start();
k++;
}
}
// Wait for all the threads
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
}
}
finally
{
// Dispose the WaitForReady
if (boardTemp != null)
{
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
if (boardTemp[i, j] != null)
{
boardTemp[i, j].Dispose();
}
}
}
}
}
}
这里的"trick"就是在64个独立的线程中创建Field
。当首先将 Field
"announce" 自身初始化给每个人时(通过设置其对应的 WaitForReady<>
对象),然后它向其邻居询问其 this
引用。它通过作为参数接收的 WaitForReady<>
对象来执行此操作。 WaitForReady<>
是一个 "special" 容器,可以包含单个值 (Value
)。如果未设置 Value
,则请求它会使线程处于等待状态,直到有人通过 set
.
设置值
对于小尺寸,可以使用递归来模拟延续来构建所有内容。对于大小为 64 的电路板,这通常是可能的。这样更快。
public class Field
{
public Field[] Neighbours;
public Field(int i, int j, int boardsize, Func<Field>[,] getters)
{
Console.WriteLine("Building [{0},{1}]", i, j);
getters[i, j] = () => this;
Neighbours = new Field[8];
Neighbours[0] = i > 0 && j > 0 ? getters[i - 1, j - 1]() : null;
Neighbours[1] = i > 0 ? getters[i - 1, j]() : null;
Neighbours[2] = i > 0 && j + 1 < boardsize ? getters[i - 1, j + 1]() : null;
Neighbours[3] = j > 0 ? getters[i, j - 1]() : null;
Neighbours[4] = j + 1 < boardsize ? getters[i, j + 1]() : null;
Neighbours[5] = i + 1 < boardsize && j > 0 ? getters[i + 1, j - 1]() : null;
Neighbours[6] = i + 1 < boardsize ? getters[i + 1, j]() : null;
Neighbours[7] = i + 1 < boardsize && j + 1 < boardsize ? getters[i + 1, j + 1]() : null;
Console.WriteLine("Builded [{0},{1}]", i, j);
}
}
public static void Main(string[] args)
{
int boardsize = 8;
Field[,] board = new Field[boardsize, boardsize];
Func<Field>[,] getters = new Func<Field>[boardsize, boardsize];
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
int i1 = i, j1 = j;
getters[i, j] = () => board[i1, j1] = new Field(i1, j1, boardsize, getters);
}
}
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
getters[i, j]();
}
}
}
另一种方法是让字段处理其他字段的创建。
编写所有代码会增加很多工作量,但处理器效率应该更高。
原来你不能在构造函数中这样做,因为你会进入一个无限循环的字段,试图相互创建(因为它们还找不到彼此)。
我喜欢做积极的内联之类的事情,尽管它在发布模式下只会产生微小的差异并且实际上会在调试模式下减慢代码速度。在我的机器上,执行 10k 次大约需要 0.1 秒。有兴趣知道这是否比@xanatos 的回答更快。这肯定是更多的代码 :P
using System.Runtime.CompilerServices;
namespace Algorithms
{
public class Board
{
public Field[,] fields = new Field[8, 8];
}
public class Field
{
// Neighbor positions
// 701 +1-1 +10 +1+1
// 6 2 0-1 00 0+1
// 543 -1-1 -10, -1+1
private Field[] neighbors = new Field[8];
public void FindNeighbors(Board board, int rank, int file)
{
if (rank > 0)
{
// Not on bottom
createNeighbor(board, rank - 1, file, 4);
if (file > 0)
{
// Not in bottom-left corner, so adding it
//createNeighbor(board, rank - 1, file, 4);
createNeighbor(board, rank - 1, file - 1, 5);
createNeighbor(board, rank, file - 1, 6);
if (rank < 7)
{
// Not on top corner, so adding above and left-above
createNeighbor(board, rank + 1, file - 1, 7);
createNeighbor(board, rank + 1, file, 0);
if (file < 7)
{
// Not in any corder, adding remainder
createNeighbor(board, rank, file + 1, 2);
createNeighbor(board, rank - 1, file + 1, 3);
createNeighbor(board, rank + 1, file + 1, 1);
}
else
{
// On the right, the only fields that haven't been added yet are on the right
}
}
else
{
// on top
if (file < 7)
{
// Not on the left, so add those fields.
createNeighbor(board, rank, file + 1, 2);
createNeighbor(board, rank - 1, file + 1, 3);
}
else
{
// On the top-left, so nothing else to add.
}
}
}
else
{
createNeighbor(board, rank, file + 1, 2);
createNeighbor(board, rank - 1, file + 1, 3);
if (rank < 7)
{
createNeighbor(board, rank + 1, file, 0);
createNeighbor(board, rank + 1, file + 1, 1);
}
else
{
// Top-left corner, nothing to add
}
}
}
else
{
// Bottom
createNeighbor(board, rank + 1, file, 0);
if (file > 0)
{
// Not on left
createNeighbor(board, rank, file - 1, 6);
createNeighbor(board, rank + 1, file - 1, 7);
if (file < 7)
{
// Not on the right
createNeighbor(board, rank + 1, file + 1, 1);
createNeighbor(board, rank, file + 1, 2);
}
}
else
{
// Bottom-left corner
createNeighbor(board, rank + 1, file + 1, 1);
createNeighbor(board, rank, file + 1, 2);
}
}
}
// Neighbor positions
// 701 +1-1 +10 +1+1
// 6 2 0-1 00 0+1
// 543 -1-1 -10, -1+1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void createNeighbor(Board board, int rank, int file, int neighbor)
{
var field = board.fields[rank, file];
if (field == null)
{
field = new Field();
board.fields[rank, file] = field;
// Start looking for neighbors after their object is initialized.
field.FindNeighbors(board, rank, file);
}
// Always copy a link to the field, even if it already existed
neighbors[neighbor] = field;
}
}
}
我有一个二维数组,它存储棋盘的字段..
private field[,] board = new field[boardsize,boardsize];
..现在当我初始化它们时,我想引用每个字段的邻居,这些字段部分尚未初始化。
目前使用这样的解决方法:
for(int x = 0; x < boardsize ; x++)
{
for(int y = 0; y < boardsize ; y++)
{
board[x,y] = new field();
}
}
for(int x = 0; x < boardsize ; x++)
{
for(int y = 0; y < boardsize ; y++)
{
board[x,y].setNeighbours(x, y, board);
}
}
这很好用,但我很感兴趣是否可以在初始化之前或初始化时设置邻居。
setNeighbour - 方法:
setNeighbour(int x, int y, field[,])
{
if(field[x-1,y] != null)
this.neighbour[0] = field[x-1,y];
if(field[x-1,y+1] != null)
this.neighbour[0] = field[x-1,y+1];
if(field[x,y+1] != null)
this.neighbour[0] = field[x,y+1];
if(field[x+1,y+1] != null)
this.neighbour[0] = field[x+1,y+1];
if(field[x+1,y] != null)
this.neighbour[0] = field[x+1,y];
if(field[x+1,y-1] != null)
this.neighbour[0] = field[x+1,y-1];
if(field[x,y-1] != null)
this.neighbour[0] = field[x,y-1];
if(field[x-1,y-1] != null)
this.neighbour[0] = field[x-1,y-1];
}
我认为不可能引用邻居,因为它们尚未创建。但是,您可以将板作为参数添加到字段的构造函数中,并在以后需要时使用 属性 查找邻居。
你可以这样做:
private field[,] board = new field[boardsize,boardsize];
for(int x = 0; x < boardsize ; x++)
{
for(int y = 0; y < boardsize ; y++)
{
if(board[x,y] == null)
board[x,y] = new field();
board[x,y].setNeighbours(x, y, board);
}
}
并以相同的方式在 setNeighbours()
方法中检查邻居的初始化:
private void setNeighbours(int x, int y, field[,] board)
{
//Initialize neighbour if not already initialized
if(board[x+1,y] == null)
board[x+1,y] = new field();
//DO SOMETHING...
}
这是一个经典问题...遗憾的是 C# 没有延续...它们可以通过使用 Thread
s 来模拟(非常缓慢)...是的,这太过分了,这只是一个编程练习:
public sealed class WaitForReady<T> : IDisposable
{
public readonly ManualResetEvent mre = new ManualResetEvent(false);
public T Value
{
get
{
mre.WaitOne();
return this.value;
}
set
{
this.value = value;
mre.Set();
}
}
private T value;
#region IDisposable Members
public void Dispose()
{
mre.Dispose();
}
#endregion
}
public class Field
{
public Field[] Neighbours;
public Field(WaitForReady<Field> me, WaitForReady<Field>[] neighbours)
{
me.Value = this;
Neighbours = new Field[neighbours.Length];
for (int i = 0; i < neighbours.Length; i++)
{
Neighbours[i] = neighbours[i] != null ? neighbours[i].Value : null;
}
}
}
public static void Main(string[] args)
{
int boardsize = 8;
Field[,] board = new Field[boardsize, boardsize];
WaitForReady<Field>[,] boardTemp = null;
try
{
boardTemp = new WaitForReady<Field>[boardsize, boardsize];
var threads = new Thread[boardsize * boardsize];
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
boardTemp[i, j] = new WaitForReady<Field>();
}
}
int k = 0;
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
var neighbours = new WaitForReady<Field>[8];
neighbours[0] = i > 0 && j > 0 ? boardTemp[i - 1, j - 1] : null;
neighbours[1] = i > 0 ? boardTemp[i - 1, j] : null;
neighbours[2] = i > 0 && j + 1 < boardsize ? boardTemp[i - 1, j + 1] : null;
neighbours[3] = j > 0 ? boardTemp[i, j - 1] : null;
neighbours[4] = j + 1 < boardsize ? boardTemp[i, j + 1] : null;
neighbours[5] = i + 1 < boardsize && j > 0 ? boardTemp[i + 1, j - 1] : null;
neighbours[6] = i + 1 < boardsize ? boardTemp[i + 1, j] : null;
neighbours[7] = i + 1 < boardsize && j + 1 < boardsize ? boardTemp[i + 1, j + 1] : null;
int i1 = i, j1 = j;
threads[k] = new Thread(() => board[i1, j1] = new Field(boardTemp[i1, j1], neighbours));
threads[k].Start();
k++;
}
}
// Wait for all the threads
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
}
}
finally
{
// Dispose the WaitForReady
if (boardTemp != null)
{
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
if (boardTemp[i, j] != null)
{
boardTemp[i, j].Dispose();
}
}
}
}
}
}
这里的"trick"就是在64个独立的线程中创建Field
。当首先将 Field
"announce" 自身初始化给每个人时(通过设置其对应的 WaitForReady<>
对象),然后它向其邻居询问其 this
引用。它通过作为参数接收的 WaitForReady<>
对象来执行此操作。 WaitForReady<>
是一个 "special" 容器,可以包含单个值 (Value
)。如果未设置 Value
,则请求它会使线程处于等待状态,直到有人通过 set
.
对于小尺寸,可以使用递归来模拟延续来构建所有内容。对于大小为 64 的电路板,这通常是可能的。这样更快。
public class Field
{
public Field[] Neighbours;
public Field(int i, int j, int boardsize, Func<Field>[,] getters)
{
Console.WriteLine("Building [{0},{1}]", i, j);
getters[i, j] = () => this;
Neighbours = new Field[8];
Neighbours[0] = i > 0 && j > 0 ? getters[i - 1, j - 1]() : null;
Neighbours[1] = i > 0 ? getters[i - 1, j]() : null;
Neighbours[2] = i > 0 && j + 1 < boardsize ? getters[i - 1, j + 1]() : null;
Neighbours[3] = j > 0 ? getters[i, j - 1]() : null;
Neighbours[4] = j + 1 < boardsize ? getters[i, j + 1]() : null;
Neighbours[5] = i + 1 < boardsize && j > 0 ? getters[i + 1, j - 1]() : null;
Neighbours[6] = i + 1 < boardsize ? getters[i + 1, j]() : null;
Neighbours[7] = i + 1 < boardsize && j + 1 < boardsize ? getters[i + 1, j + 1]() : null;
Console.WriteLine("Builded [{0},{1}]", i, j);
}
}
public static void Main(string[] args)
{
int boardsize = 8;
Field[,] board = new Field[boardsize, boardsize];
Func<Field>[,] getters = new Func<Field>[boardsize, boardsize];
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
int i1 = i, j1 = j;
getters[i, j] = () => board[i1, j1] = new Field(i1, j1, boardsize, getters);
}
}
for (int i = 0; i < boardsize; i++)
{
for (int j = 0; j < boardsize; j++)
{
getters[i, j]();
}
}
}
另一种方法是让字段处理其他字段的创建。 编写所有代码会增加很多工作量,但处理器效率应该更高。
原来你不能在构造函数中这样做,因为你会进入一个无限循环的字段,试图相互创建(因为它们还找不到彼此)。
我喜欢做积极的内联之类的事情,尽管它在发布模式下只会产生微小的差异并且实际上会在调试模式下减慢代码速度。在我的机器上,执行 10k 次大约需要 0.1 秒。有兴趣知道这是否比@xanatos 的回答更快。这肯定是更多的代码 :P
using System.Runtime.CompilerServices;
namespace Algorithms
{
public class Board
{
public Field[,] fields = new Field[8, 8];
}
public class Field
{
// Neighbor positions
// 701 +1-1 +10 +1+1
// 6 2 0-1 00 0+1
// 543 -1-1 -10, -1+1
private Field[] neighbors = new Field[8];
public void FindNeighbors(Board board, int rank, int file)
{
if (rank > 0)
{
// Not on bottom
createNeighbor(board, rank - 1, file, 4);
if (file > 0)
{
// Not in bottom-left corner, so adding it
//createNeighbor(board, rank - 1, file, 4);
createNeighbor(board, rank - 1, file - 1, 5);
createNeighbor(board, rank, file - 1, 6);
if (rank < 7)
{
// Not on top corner, so adding above and left-above
createNeighbor(board, rank + 1, file - 1, 7);
createNeighbor(board, rank + 1, file, 0);
if (file < 7)
{
// Not in any corder, adding remainder
createNeighbor(board, rank, file + 1, 2);
createNeighbor(board, rank - 1, file + 1, 3);
createNeighbor(board, rank + 1, file + 1, 1);
}
else
{
// On the right, the only fields that haven't been added yet are on the right
}
}
else
{
// on top
if (file < 7)
{
// Not on the left, so add those fields.
createNeighbor(board, rank, file + 1, 2);
createNeighbor(board, rank - 1, file + 1, 3);
}
else
{
// On the top-left, so nothing else to add.
}
}
}
else
{
createNeighbor(board, rank, file + 1, 2);
createNeighbor(board, rank - 1, file + 1, 3);
if (rank < 7)
{
createNeighbor(board, rank + 1, file, 0);
createNeighbor(board, rank + 1, file + 1, 1);
}
else
{
// Top-left corner, nothing to add
}
}
}
else
{
// Bottom
createNeighbor(board, rank + 1, file, 0);
if (file > 0)
{
// Not on left
createNeighbor(board, rank, file - 1, 6);
createNeighbor(board, rank + 1, file - 1, 7);
if (file < 7)
{
// Not on the right
createNeighbor(board, rank + 1, file + 1, 1);
createNeighbor(board, rank, file + 1, 2);
}
}
else
{
// Bottom-left corner
createNeighbor(board, rank + 1, file + 1, 1);
createNeighbor(board, rank, file + 1, 2);
}
}
}
// Neighbor positions
// 701 +1-1 +10 +1+1
// 6 2 0-1 00 0+1
// 543 -1-1 -10, -1+1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void createNeighbor(Board board, int rank, int file, int neighbor)
{
var field = board.fields[rank, file];
if (field == null)
{
field = new Field();
board.fields[rank, file] = field;
// Start looking for neighbors after their object is initialized.
field.FindNeighbors(board, rank, file);
}
// Always copy a link to the field, even if it already existed
neighbors[neighbor] = field;
}
}
}