Unity Editor freezing while running timers - c#

I'm taking a coursera course where an assignment I'm working on involves completing work on a "take the last rock" style puzzle. For this assignment, the puzzle needs to run with two AI players 600 times at varying levels of AI difficulty. The problem is that after implementing my changes (which involve recursive Minimax algorithms) I'm running into an issue where the unity editor seems to freeze after a few games. It always seems to happen while the newgamedelaytimer is running (timer code provided by the instructor). I've watched it run in debug mode and after a few games the timer stops incrementing and I have to restart Unity.
Here is the code for the base game (where the newgamedelaytimer is called) and the course provided timer.
Is there something I'm missing here?
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
/// <summary>
/// Game manager
/// </summary>
public class DontTakeTheLastTeddy : MonoBehaviour, ITakeTurnInvoker, IGameOverInvoker,
IGameStartingInvoker
{
Board board;
Player player1;
Player player2;
// multiple games support
Timer newGameDelayTimer;
PlayerName firstMovePlayer = PlayerName.Player1;
const int TotalGames = 600;
int gamesPlayed = 0;
// events invoked by class
TakeTurn takeTurnEvent = new TakeTurn();
GameOver gameOverEvent = new GameOver();
GameStarting gameStartingEvent = new GameStarting();
#region Constructor
// Uncomment the code below after copying this class into the console
// app for the automated grader. DON'T uncomment it now; it won't
// compile in a Unity project
/// <summary>
/// Constructor
///
/// Note: The class in the Unity solution doesn't use a constructor;
/// this constructor is to support automated grading
/// </summary>
/// <param name="gameObject">the game object the script is attached to</param>
//public DontTakeTheLastTeddy(GameObject gameObject) :
// base(gameObject)
//{
//}
#endregion
/// <summary>
/// Awake is called before Start
///
/// Leave this method public to support automated grading
/// </summary>
public void Awake()
{
// retrieve board and player references
board = GameObject.FindGameObjectWithTag("Board").GetComponent<Board>();
player1 = GameObject.FindGameObjectWithTag("Player1").GetComponent<Player>();
player2 = GameObject.FindGameObjectWithTag("Player2").GetComponent<Player>();
// register as invoker and listener
EventManager.AddTakeTurnInvoker(this);
EventManager.AddGameOverInvoker(this);
EventManager.AddGameStartingInvoker(this);
EventManager.AddTurnOverListener(HandleTurnOverEvent);
EventManager.AddGameOverListener(HandleGameOverEvent);
// set up timer for delay between games
newGameDelayTimer = gameObject.AddComponent<Timer>();
newGameDelayTimer.Duration = 1.5f;
newGameDelayTimer.AddTimerFinishedListener(HandleTimerFinishedEvent);
// initialize statistics class
Statistics.Initialize();
}
private void HandleGameOverEvent(PlayerName arg0, Difficulty arg1, Difficulty arg2)
{
Debug.Log("Starting New Game Delay timer");
newGameDelayTimer.Run();
//HandleTimerFinishedEvent();
}
/// <summary>
/// Use this for initialization
/// </summary>
void Start()
{
StartGame(PlayerName.Player1, Difficulty.Easy, Difficulty.Easy);
}
/// <summary>
/// Adds the given listener for the TakeTurn event
/// </summary>
/// <param name="listener">listener</param>
public void AddTakeTurnListener(UnityAction<PlayerName, Configuration> listener)
{
takeTurnEvent.AddListener(listener);
}
/// <summary>
/// Adds the given listener for the GameOver event
/// </summary>
/// <param name="listener">listener</param>
public void AddGameOverListener(
UnityAction<PlayerName, Difficulty, Difficulty> listener)
{
gameOverEvent.AddListener(listener);
}
/// <summary>
/// Adds the given listener for the GameStarting event
/// </summary>
/// <param name="listener">listener</param>
public void AddGameStartingListener(UnityAction listener)
{
gameStartingEvent.AddListener(listener);
}
/// <summary>
/// Starts a game with the given player taking the
/// first turn
///
/// Leave this method public to support automated grading
/// </summary>
/// <param name="firstPlayer">player taking first turn</param>
/// <param name="player1Difficulty">difficulty for player 1</param>
/// <param name="player2Difficulty">difficulty for player 2</param>
public void StartGame(PlayerName firstPlayer, Difficulty player1Difficulty,
Difficulty player2Difficulty)
{
// set player difficulties
player1.Difficulty = player1Difficulty;
player2.Difficulty = player2Difficulty;
// create new board
board.CreateNewBoard();
takeTurnEvent.Invoke(firstPlayer,
board.Configuration);
}
/// <summary>
/// Handles the TurnOver event by having the
/// other player take their turn
/// </summary>
/// <param name="player">who finished their turn</param>
/// <param name="newConfiguration">the new board configuration</param>
void HandleTurnOverEvent(PlayerName player,
Configuration newConfiguration)
{
board.Configuration = newConfiguration;
// check for game over
if (newConfiguration.Empty)
{
// fire event with winner
if (player == PlayerName.Player1)
{
gameOverEvent.Invoke(PlayerName.Player2, player1.Difficulty, player2.Difficulty);
}
else
{
gameOverEvent.Invoke(PlayerName.Player1, player1.Difficulty, player2.Difficulty);
}
}
else
{
// game not over, so give other player a turn
if (player == PlayerName.Player1)
{
takeTurnEvent.Invoke(PlayerName.Player2,
newConfiguration);
}
else
{
takeTurnEvent.Invoke(PlayerName.Player1,
newConfiguration);
}
}
}
/// <summary>
/// Starts a new game when the new game delay timer finishes
/// </summary>
void HandleTimerFinishedEvent()
{
Debug.Log("Finished new Game Timer");
// constant provided for autograder support
if (!GameConstants.PlaySingleGame)
{
if (gamesPlayed < TotalGames)
{
if (gamesPlayed % 100 == 0)
{
// uncomment the line below and implement a
// SetPlayerDifficulties method
SetPlayerDifficulties(gamesPlayed);
}
gamesPlayed++;
//Debug.Log("Games Played: " + gamesPlayed);
// alternate player making first move in game
if (firstMovePlayer == PlayerName.Player1)
{
firstMovePlayer = PlayerName.Player2;
}
else
{
firstMovePlayer = PlayerName.Player1;
}
StartGame(firstMovePlayer, player1.Difficulty, player2.Difficulty);
}
else
{
// move to statistics scene when all games have been played
SceneManager.LoadScene("statistics");
}
}
}
private void SetPlayerDifficulties(int gamesPlayed)
{
if (gamesPlayed == 100)
{
player1.Difficulty = Difficulty.Medium;
player2.Difficulty = Difficulty.Medium;
}
else if (gamesPlayed == 200)
{
player1.Difficulty = Difficulty.Hard;
player2.Difficulty = Difficulty.Hard;
}
else if (gamesPlayed == 300)
{
player1.Difficulty = Difficulty.Easy;
player2.Difficulty = Difficulty.Medium;
}
else if (gamesPlayed == 400)
{
player2.Difficulty = Difficulty.Hard;
}
else if (gamesPlayed == 500)
{
player1.Difficulty = Difficulty.Medium;
player2.Difficulty = Difficulty.Hard;
}
}
}
Timer.cs
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// A timer
/// </summary>
public class Timer : MonoBehaviour
{
#region Fields
// timer duration
float totalSeconds = 0;
// timer execution
float elapsedSeconds = 0;
bool running = false;
// support for TimerFinished event
bool started = false;
TimerFinished timerFinished = new TimerFinished();
#endregion
#region Properties
/// <summary>
/// Sets the duration of the timer
/// The duration can only be set if the timer isn't currently running
/// </summary>
/// <value>duration</value>
public float Duration
{
set
{
if (!running)
{
totalSeconds = value;
}
}
}
/// <summary>
/// Gets whether or not the timer has finished running
/// This property returns false if the timer has never been started
/// </summary>
/// <value>true if finished; otherwise, false.</value>
public bool Finished
{
get { return started && !running; }
}
/// <summary>
/// Gets whether or not the timer is currently running
/// </summary>
/// <value>true if running; otherwise, false.</value>
public bool Running
{
get { return running; }
}
/// <summary>
/// Gets ho wmany seconds are left on the timer
/// </summary>
/// <value>seconds left</value>
public float SecondsLeft
{
get
{
if (running)
{
return totalSeconds - elapsedSeconds;
}
else
{
return 0;
}
}
}
#endregion
#region Methods
/// <summary>
/// Update is called once per frame
/// </summary>
void Update()
{
// update timer and check for finished
if (running)
{
elapsedSeconds += Time.fixedDeltaTime;
if (elapsedSeconds >= totalSeconds)
{
running = false;
timerFinished.Invoke();
}
}
}
/// <summary>
/// Runs the timer
/// Because a timer of 0 duration doesn't really make sense,
/// the timer only runs if the total seconds is larger than 0
/// This also makes sure the consumer of the class has actually
/// set the duration to something higher than 0
/// </summary>
public void Run()
{
// only run with valid duration
if (totalSeconds > 0)
{
started = true;
running = true;
elapsedSeconds = 0;
}
}
/// <summary>
/// Stops the timer
/// </summary>
public void Stop()
{
started = false;
running = false;
}
/// <summary>
/// Adds the given number of seconds to the timer
/// </summary>
/// <param name="seconds">time to add</param>
public void AddTime(float seconds)
{
totalSeconds += seconds;
}
/// <summary>
/// Adds the given listener for the TimerFinished event
/// </summary>
/// <param name="listener">listener</param>
public void AddTimerFinishedListener(UnityAction listener)
{
timerFinished.AddListener(listener);
}
#endregion
}

Related

I get invalid return on a Kitty problem in C#

Hi everyone. The problem is too long for me to paste it here so I will paste a URL instead. I get 50% invalid returns. I added a check if a cat has hit a deadlock to collect food/soul on that cell. I have been tackling this issue since 6 am and it is 11am now and I am getting to the point of frustration.
Briefly the problem is the following:
Your task is to calculate the food and the souls collected by Kitty or to output that she is deadlocked.
On the first line of the input you will receive the positions of the coder souls ("#"), food ("*") and deadlocks ("x") as string.
On the second line of the input you will receive the path of the Kitty as string with integers separated by single space. Positive means, move to the right, negative means, move to the left.
The starting position will always be at index 0.
The final result is either the souls, food and deadlocks count or a string informing that the Kitty is deadlocked. The format is shown in the zero tests and the example.
# - symbol for coder soul
* - symbol for food
x - symbol for deadlock
More details below:
https://judge.telerikacademy.com/problem/30kitty
string input = Console.ReadLine();
int deadlocks = 0;
string input2 = Console.ReadLine();
string[] output = input2.Split(' ');
int position = 0;
int startposition = 0;
int codersoulscollected = 0;
int foodcollected = 0;
int iterations = Math.Max(input.Length, output.Length);
bool[] isCollected = new bool[input.Length];
for (int i = 0; i <= iterations; i++)
{
startposition += position;
if (startposition < 0)
{
startposition = input.Length + startposition;
}
if (startposition >= input.Length)
{
startposition = startposition - input.Length;
}
char a = input[startposition];
if (a == '#' && (isCollected[startposition] == false))
{
codersoulscollected++;
isCollected[startposition] = true;
}
if (a == '*' && (isCollected[startposition] == false))
{
foodcollected++;
isCollected[startposition] = true;
}
if (a == 'x' && (isCollected[startposition] == false))
{
deadlocks++;
if (startposition % 2 == 0)
{
codersoulscollected--;
isCollected[startposition] = true;
}
else
{
foodcollected--;
isCollected[startposition] = true;
}
}
else if (a == 'x' && (isCollected[startposition] == true))
{
if (startposition % 2 == 0)
{
codersoulscollected++;
}
else
{
foodcollected++;
}
}
if (output.Length == i)
{
break;
}
position = int.Parse(output[i]);
if (foodcollected < 0 || codersoulscollected < 0)
{
Console.WriteLine("You are deadlocked, you greedy kitty!");
Console.WriteLine($"Jumps before deadlock: {i}");
return;
}
}
if (foodcollected >= 0 || codersoulscollected >= 0)
{
Console.WriteLine($"Coder souls collected: {codersoulscollected}\r\nFood collected: {foodcollected}\r\nDeadlocks: {deadlocks}");
}
Since I had some time at my hand, I wrote a simple solution for you which walks you step by step in an OOP manner. Hopefully you can see your problem as well.
This is your cat. It can walk the given amount of steps on a given Path. It also collects food and soul etc.
public class Cat
{
/// <summary>
/// Amount of collected coder souls.
/// </summary>
private int _coderSouls;
/// <summary>
/// Amount of collected food.
/// </summary>
private int _food;
/// <summary>
/// Amount of deadlocks collected.
/// </summary>
private int _deadlocks;
/// <summary>
/// Number of jumps before deadlocking.
/// Starts from -1 because When we set the path
/// The kitty starts from the 0th tile.
/// </summary>
private int _numberOfJumps = -1;
/// <summary>
/// If Cat can still move.
/// </summary>
private bool _deadLocked;
/// <summary>
/// Path to follow.
/// </summary>
private Path _path;
/// <summary>
/// Creates a Kitty
/// </summary>
/// <param name="path">Path for Kitty</param>
public Cat(Path path)
{
SetPath(path);
}
/// <summary>
/// Set the path for Kitty to follow.
/// </summary>
/// <param name="path">Path to follow.</param>
private void SetPath(Path path)
{
_path = path;
Walk(0);
}
/// <summary>
/// Walks the Kitty with the given amount of steps.
/// </summary>
/// <param name="step">Amount of steps</param>
/// <returns>If kitty can move any more.</returns>
public bool Walk(int step)
{
// If Kitty is deadlocked it can not move any more
if (_deadLocked)
{
return false;
}
// Walks the cat with the given step amount
var got = _path.MoveToNext(step);
// Increase the number of Jumps
_numberOfJumps++;
// Rule written in the question
switch (got)
{
case ItemType.CoderSoul:
_coderSouls++;
break;
case ItemType.Food:
_food++;
break;
case ItemType.DeadLock:
_deadlocks++;
var isEven = _path.GetPosition() % 2 == 0;
if (isEven)
{
if (_coderSouls > 0)
{
_coderSouls--;
return true;
}
_deadLocked = true;
return false;
}
if (_food > 0)
{
_food--;
return true;
}
_deadLocked = true;
return false;
}
return true;
}
/// <summary>
/// When Kitty finished moving, Gets Summary.
/// </summary>
/// <returns>Summary of movemebt</returns>
public string Summarize()
{
return _deadLocked ? PrepareDeadLockMessage() : PrepareSummaryMessage();
}
/// <summary>
/// Deadlock message.
/// </summary>
/// <returns>Deadlock message.</returns>
private string PrepareDeadLockMessage()
{
return $"You are deadlocked, you greedy kitty!{Environment.NewLine}Jumps before deadlock: {_numberOfJumps}";
}
/// <summary>
/// Normal finish.
/// </summary>
/// <returns>Normal finish.</returns>
private string PrepareSummaryMessage()
{
return $"Coder souls collected: {_coderSouls}{Environment.NewLine}Food collected: {_food}{Environment.NewLine}Deadlocks: {_deadlocks}";
}
}
This is your path. You have to parse it as it was given in the question.
public class Path
{
private readonly Item[] path;
private int _currentIndex;
public Path(string pathElements)
{
path = pathElements.Select(t => new Item(t)).ToArray();
_currentIndex = 0;
}
public ItemType MoveToNext(int increase)
{
_currentIndex += increase;
if (_currentIndex > path.Length)
{
_currentIndex -= path.Length;
}
if (_currentIndex < 0)
{
_currentIndex += path.Length;
}
return path[_currentIndex].Collect();
}
public int GetPosition()
{
return _currentIndex;
}
}
This is your single item in your given cell.
public class Item
{
/// <summary>
/// Kitty already collected this cell or not?
/// </summary>
public bool IsCollected { get; private set; }
/// <summary>
/// ItemType in this cell
/// </summary>
public ItemType ItemType { get; }
/// <summary>
/// Represents a single item in each cell.
/// </summary>
/// <param name="c">Type of the item decided by char.</param>
public Item(char c)
{
switch (c)
{
case '#':
ItemType = ItemType.CoderSoul;
break;
case '*':
ItemType = ItemType.Food;
break;
case 'x':
ItemType = ItemType.DeadLock;
break;
}
}
/// <summary>
/// Collect the item in this cell.
/// </summary>
/// <returns>The collected item.</returns>
public ItemType Collect()
{
if (IsCollected)
{
return ItemType.None;
}
IsCollected = true;
return ItemType;
}
}
And finally this is your ItemTypes that can be contained in each cell
/// <summary>
/// The type of item located in each single cell.
/// </summary>
public enum ItemType
{
None,
CoderSoul,
Food,
DeadLock,
}
This is how you use this example. Please go through each step with a debugger.
var cat = new Cat(new Path("x#*#*#*"));
var walkinOrder = "1 -1 -1 4";
var intOrder = walkinOrder.Split(' ').Select(int.Parse);
foreach (var step in intOrder) {
if (cat.Walk(step) == false)
{
break;
}
}
Console.WriteLine(cat.Summarize());

Moving a cube with the Gear VR controller in Unity

I'm trying to make a Gear VR application with the Gear VR controller and the Oculus SDK.
I got the GazePointerRing to work with the controller prefab. There is a reticle in my app visible that i can move around with the Gear VR controller. It detects the cubes that I've placed in the scene.
What I want to do now is point the reticle at a cube and hold a button on the controller, so that the cube will stick to my controller model and can move around until I let go of the button. I've been searching in the OVR Physics Raycaster script how i can call upon the raycast hit and input it in a button command in an if statement. But i can't find a way to call upon the raycast hit with an object.
This is the Oculus code in the OVR Physics Raycaster script:
using System.Collections.Generic;
namespace UnityEngine.EventSystems
{
/// <summary>
/// Simple event system using physics raycasts.
/// </summary>
[RequireComponent(typeof(OVRCameraRig))]
public class OVRPhysicsRaycaster : BaseRaycaster
{
/// <summary>
/// Const to use for clarity when no event mask is set
/// </summary>
protected const int kNoEventMaskSet = -1;
/// <summary>
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
/// </summary>
[SerializeField]
public LayerMask m_EventMask = kNoEventMaskSet;
protected OVRPhysicsRaycaster()
{ }
public override Camera eventCamera
{
get
{
return GetComponent<OVRCameraRig>().leftEyeCamera;
}
}
/// <summary>
/// Depth used to determine the order of event processing.
/// </summary>
public virtual int depth
{
get { return (eventCamera != null) ? (int)eventCamera.depth : 0xFFFFFF; }
}
/// <summary>
/// Event mask used to determine which objects will receive events.
/// </summary>
public int finalEventMask
{
get { return (eventCamera != null) ? eventCamera.cullingMask & m_EventMask : kNoEventMaskSet; }
}
/// <summary>
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
/// </summary>
public LayerMask eventMask
{
get { return m_EventMask; }
set { m_EventMask = value; }
}
/// <summary>
/// Perform a raycast using the worldSpaceRay in eventData.
/// </summary>
/// <param name="eventData"></param>
/// <param name="resultAppendList"></param>
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
// This function is closely based on PhysicsRaycaster.Raycast
if (eventCamera == null)
return;
OVRRayPointerEventData rayPointerEventData = eventData as OVRRayPointerEventData;
if (rayPointerEventData == null)
return;
var ray = rayPointerEventData.worldSpaceRay;
float dist = eventCamera.farClipPlane - eventCamera.nearClipPlane;
var hits = Physics.RaycastAll(ray, dist, finalEventMask);
if (hits.Length > 1)
System.Array.Sort(hits, (r1, r2) => r1.distance.CompareTo(r2.distance));
if (hits.Length != 0)
{
for (int b = 0, bmax = hits.Length; b < bmax; ++b)
{
var result = new RaycastResult
{
gameObject = hits[b].collider.gameObject,
module = this,
distance = hits[b].distance,
index = resultAppendList.Count,
worldPosition = hits[0].point,
worldNormal = hits[0].normal,
};
resultAppendList.Add(result);
}
}
}
/// <summary>
/// Perform a Spherecast using the worldSpaceRay in eventData.
/// </summary>
/// <param name="eventData"></param>
/// <param name="resultAppendList"></param>
/// <param name="radius">Radius of the sphere</param>
public void Spherecast(PointerEventData eventData, List<RaycastResult> resultAppendList, float radius)
{
if (eventCamera == null)
return;
OVRRayPointerEventData rayPointerEventData = eventData as OVRRayPointerEventData;
if (rayPointerEventData == null)
return;
var ray = rayPointerEventData.worldSpaceRay;
float dist = eventCamera.farClipPlane - eventCamera.nearClipPlane;
var hits = Physics.SphereCastAll(ray, radius, dist, finalEventMask);
if (hits.Length > 1)
System.Array.Sort(hits, (r1, r2) => r1.distance.CompareTo(r2.distance));
if (hits.Length != 0)
{
for (int b = 0, bmax = hits.Length; b < bmax; ++b)
{
var result = new RaycastResult
{
gameObject = hits[b].collider.gameObject,
module = this,
distance = hits[b].distance,
index = resultAppendList.Count,
worldPosition = hits[0].point,
worldNormal = hits[0].normal,
};
resultAppendList.Add(result);
}
}
}
/// <summary>
/// Get screen position of this world position as seen by the event camera of this OVRPhysicsRaycaster
/// </summary>
/// <param name="worldPosition"></param>
/// <returns></returns>
public Vector2 GetScreenPos(Vector3 worldPosition)
{
// In future versions of Uinty RaycastResult will contain screenPosition so this will not be necessary
return eventCamera.WorldToScreenPoint(worldPosition);
}
}
}
prerequisite: Ensure to have OVR Manager in your scene, it is a singleton and required for the GearVR controller (OVRInput class) to work.
My usual approach is to raycast a ray from the controller anchor position going forward and check if it hits a desired object
public class SampleScript : MonoBehaviour
{
public Transform anchorPos;
public GameObject detectionLineObject; // a gameObject with a line renderer
private RaycastHit _hitInfo;
private LineRenderer _detectionLine;
void Start()
{
GameObject line = Instantiate(detectionLineObject);
_detectionLine = line.GetComponent<LineRenderer>();
}
void Update()
{
DetectionManager();
}
void DetectionManager()
{
// check if controller is actually connected
if (!OVRInput.IsControllerConnected(OVRInput.Controller.RTrackedRemote) || !OVRInput.IsControllerConnected(OVRInput.Controller.LTrackedRemote))
{
return;
}
// launch a ray from the OVRCameraRig's Anchor Right
if (Physics.Raycast(anchorPos.position, anchorPos.forward, out _hitInfo))
{
// set our line points
_detectionLine.SetPosition(0, anchorPos.position);
_detectionLine.SetPosition(1, _hitInfo.point);
MyComponent target = _hitInfo.collider.gameObject.GetComponent<MyComponent>();
if (target != null)
{
// Do you stuff here
target.StartInteraction();
}
}
else
{
// point our line to infinity
_detectionLine.SetPosition(0, anchorPos.position);
_detectionLine.SetPosition(1, anchorPos.forward * 500.0f);
}
}
}

Memory efficient data structure for high performance

I have a need for a circular buffer (or other data structure), on which I can do a "ToArray" or similar call to get the current state of the buffer and use whilst the buffer carries on possibly overwriting the values held.
The reason for this use case is that the returned data is passed to a worker thread to process and the idea is to ensure that as little data is overwritten as possible in between the calls. The choice of a circular data structure was ostensibly to reduce the memory usage of the buffer to a fixed value.
I built the data structure below, which up until yesterday was sufficient for my needs. The relevant calls are TakeSnapshot, TakeAll and TakeWeakArraySnapshot which are all variations on the same theme.
The call is made quite frequently when about a 1000 samples of reference types are available.
All the calls result in an out of memory exception at some point. The exception is the TakeWeakArraySnapshot which basically reverts to null midway through using the array (something i guess is to do with the fact that the gc handle is weak?)
Is there a more suitable memory efficient data structure for this use or something I am doing wrong? Would changing the gc handle type to normal help. Is it possible to create a output type that wraps the references (like i attempted to with the weakreference) which would be easily be reclaimed by the garbage collector?
Thanks for reading.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Threading;
using System.Runtime.InteropServices;
public class WeakArray<T>
{
private GCHandle[] fArray;
public WeakArray(int length)
{
fArray = new GCHandle[length];
for (int i = 0; i < length; i++)
fArray[i] = GCHandle.Alloc(null, GCHandleType.Weak);
}
~WeakArray()
{
if (fArray == null)
return;
int count = fArray.Length;
for (int i = 0; i < count; i++)
{
GCHandle gcHandle = fArray[i];
if (!gcHandle.IsAllocated)
break;
gcHandle.Free();
}
}
public int Length
{
get
{
return fArray.Length;
}
}
public T this[int index]
{
get
{
return (T) fArray[index].Target;
}
set
{
fArray[index].Target = value;
}
}
}
public class OutputData<TDataType>:IEnumerable<TDataType>,IDisposable
{
private TDataType[] Buffer { get; set; }
private readonly object _lock = new object();
public OutputData(ref TDataType[] buffer,long length)
{
Buffer = buffer;
Length = length;
}
public long Length { get; private set; }
/// <summary>
/// Gets the <see cref="`0"/> at the specified index. Throws IndexOutOfRange for an invalid index
/// or returns the default value of the generic type on an empty queue
/// </summary>
/// <value>
/// The <see cref="`0"/>.
/// </value>
/// <param name="i">The item at that index.</param>
/// <returns></returns>
public TDataType this[int i]
{
get
{
lock (_lock)
{
return Length > 0 ? Buffer[i] : default(TDataType);
}
}
}
public IEnumerator<TDataType> GetEnumerator()
{
for (int i = 0; i < Length; i++)
{
yield return Buffer[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Dispose()
{
Array.Clear(Buffer,0,Buffer.Length);
}
public void SetLength(int count)
{
Length = count;
}
}
/// <summary>
/// Implements a CircularBuffer that behaves as a queue
/// </summary>
/// <typeparam name="T"></typeparam>
public class FixedCapacityQueue<T>
{
private T[] _buffer;
private T[] _output;
private OutputData<T> _outputData;
/// <summary>
/// Gets the dropped count. This is the number of samples that have been dropped to
/// maintain the fixed size of the datastructure.
/// </summary>
/// <value>
/// The dropped count.
/// </value>
public int DroppedCount { get; protected set; }
/// <summary>
/// The default number of dropped items required to generate a report
/// </summary>
protected const int DroppedFramesBetweenReports = 1000;
/// <summary>
/// The _start. Index of the first element in buffer.
/// </summary>
private int _start;
/// <summary>
/// The _end. Index after the last element in the buffer.
/// </summary>
private int _end;
/// <summary>
/// The _size. Buffer size.
/// </summary>
private int _numberOfItemsInBuffer;
private readonly object _lock = new object();
/// <summary>
/// Gets or sets the name of the buffer.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; protected set; }
private readonly T _default = default(T);
/// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityQueue{T}" /> class.
/// </summary>
/// <param name="name">The name of the queue.</param>
/// <param name="bufferSize">Size of the buffer.</param>
public FixedCapacityQueue(string name, int bufferSize)
{
Contract.Requires(bufferSize > 0);
Contract.Requires(!String.IsNullOrEmpty(name));
_buffer = new T[bufferSize];
_output = new T[bufferSize];
_outputData = new OutputData<T>(ref _output, 0);
_start = 0;
_end = _numberOfItemsInBuffer == bufferSize ? 0 : _numberOfItemsInBuffer;
Name = String.Format("FixedCapacityQueue for {0}", name);
}
/// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityQueue{T}" /> class.
/// </summary>
/// <param name="name">The nameof the buffer.</param>
/// <param name="bufferSize">Size of the buffer.</param>
/// <param name="data">The data to be added to the queue.</param>
/// <exception cref="System.ArgumentException"></exception>
public FixedCapacityQueue(string name, int bufferSize, ICollection<T> data)
: this(name, bufferSize)
{
Contract.Requires(data != null);
Contract.Requires(bufferSize > 0);
Contract.Requires(data.Count < bufferSize);
foreach (var dataItem in data)
{
Enqueue(dataItem);
}
}
/// <summary>
/// Gets a value indicating whether the queue [is empty].
/// </summary>
/// <value>
/// <c>true</c> if [is empty]; otherwise, <c>false</c>.
/// </value>
public bool IsEmpty
{
get
{
lock (_lock)
{
return _numberOfItemsInBuffer == 0;
}
}
}
/// <summary>
/// Gets a value indicating whether the queue [is full].
/// </summary>
/// <value>
/// <c>true</c> if [is full]; otherwise, <c>false</c>.
/// </value>
public bool IsFull
{
get
{
lock (_lock)
{
return Count == Size;
}
}
}
/// <summary>
/// Gets the number of items currently present in the queue.
/// </summary>
/// <value>
/// The count.
/// </value>
public int Count
{
get
{
lock (_lock)
{
return _numberOfItemsInBuffer;
}
}
}
/// <summary>
/// Gets the declared size of the queue.
/// </summary>
/// <value>
/// The size.
/// </value>
public int Size
{
get
{
lock (_lock)
{
return _buffer.Length;
}
}
}
/// <summary>
/// Dequeues an item from the queue. The expected behaviour is that if a Dequeue operation is
/// requested whilst a queue is empty, the default type of the generic queue is returned.
/// </summary>
/// <returns></returns>
public T Dequeue()
{
lock (_lock)
{
if (IsEmpty) return _default;
var item = _buffer[_start];
_buffer[_start] = _default;
Increment(ref _start);
--_numberOfItemsInBuffer;
return item;
}
}
/// <summary>
/// Enqueues the specified item to the queue.
/// </summary>
/// <param name="toAdd">To add.</param>
public void Enqueue(T toAdd)
{
lock (_lock)
{
if (IsFull)
{
_buffer[_end] = toAdd;
Increment(ref _end);
_start = _end;
DroppedCount++;
//report drops
if (DroppedCount >= DroppedFramesBetweenReports)
{
//EventAndErrorPump.Instance.WriteOnce(1000, ReportLevelEnum.Warning,
// String.Format("{0} FixedQueue Dropped Items: {1} ", Name, DroppedCount));
DroppedCount = 0;
}
}
else
{
_buffer[_end] = toAdd;
Increment(ref _end);
++_numberOfItemsInBuffer;
}
}
}
/// <summary>
/// Increments the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Increment(ref int index)
{
if (++index == Size)
{
index = 0;
}
}
/// <summary>
/// Decrements the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Decrement(ref int index)
{
if (index == 0)
{
index = Size;
}
index--;
}
public void Enqueue(IEnumerable<T> toAdd)
{
lock (_lock)
{
foreach (var dataItem in toAdd)
{
Enqueue(dataItem);
}
}
}
public IEnumerator<T> GetEnumerator()
{
var segments = new ArraySegment<T>[2] {ArrayOne(), ArrayTwo()};
foreach (ArraySegment<T> segment in segments)
{
for (int i = 0; i < segment.Count; i++)
{
yield return segment.Array[segment.Offset + i];
}
}
}
/// <summary>
/// Gets the at the specified index. Throws IndexOutOfRange for an invalid index
/// or returns the default value of the generic type on an empty queue. The head/earliest item index can also be
/// null in the event of a dequeue. If the front of the queue is required,please use the front function instead
/// </summary>
/// <param name="i">The item at that index.</param>
/// <returns></returns>
/// <exception cref="System.IndexOutOfRangeException"></exception>
public T this[int index]
{
get
{
lock (_lock)
{
if (IsEmpty)
{
return _default;
}
if (index >= _numberOfItemsInBuffer)
{
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}",
index, _numberOfItemsInBuffer));
}
int actualIndex = InternalIndex(index);
return _buffer[actualIndex];
}
}
}
/// <summary>
/// Converts the index in the argument to an index in <code>_buffer</code>
/// </summary>
/// <returns>
/// The transformed index.
/// </returns>
/// <param name='index'>
/// External index.
/// </param>
private int InternalIndex(int index)
{
return _start + (index < (Size - _start) ? index : index - Size);
}
/// <summary>
/// Clears this instance.
/// </summary>
public void Clear()
{
lock (_lock)
{
_numberOfItemsInBuffer = 0;
_start = 0;
_end = 0;
}
}
/// <summary>
/// Takes a snapshot of the queue and returns it. If used in isolation .i.e not in a buffer implementation
/// it will return all the elements of the queue and leave the queue empty.
/// In a buffer implementation , since the buffer could be accepting data at the point of this call; it is a stale
/// snapshot of the queue when the call is made.
/// </summary>
/// <returns></returns>
[Obsolete("Use TakeAllData() instead")]
public T[] TakeSnapshot()
{
lock (_lock)
{
return TakeSnapshot(Count);
}
}
/// <summary>
/// Takes all data available in the queue.
/// </summary>
/// <returns></returns>
public OutputData<T> TakeAll()
{
var count = Count;
if (count <= 0) return null;
lock (_lock)
{
CopyInto(count, _output);
_outputData.SetLength(count);
return _outputData;
}
}
/// <summary>
/// Takes a snapshot of the queue using the count to limit the output to return. In the event that the specified
/// count is larger than the current size of the queue, it will throw an exception. A zero count value will immediately return
/// In a buffer implementation , since the buffer could be accepting data at the point of this call; it is a stale
/// snapshot of the queue when the call is made.
/// </summary>
/// <returns></returns>
[Obsolete("Use TakeAllData(int count) instead")]
public T[] TakeSnapshot(int count)
{
if (count == 0) return null;
lock (_lock)
{
if (count > _numberOfItemsInBuffer)
{
count = _numberOfItemsInBuffer;
//throw new ArgumentOutOfRangeException(String.Format("Queue size is {0}", Size));
}
var output = new T[count];
CopyInto(count, output);
return output;
}
}
private void CopyInto(int count, T[] output)
{
var lastIndex = (_start + count) - 1;
if (lastIndex >= Size)
{
Array.Copy(_buffer, _start, output, 0, Size - _start);
Array.Copy(_buffer, 0, output, Size - _start, (lastIndex%Size) + 1);
}
else
{
Array.Copy(_buffer, _start, output, 0, count);
}
_start = (_start + count)%Size;
_numberOfItemsInBuffer = _numberOfItemsInBuffer - count;
}
public T[] PeekSnapshot()
{
lock (_lock)
{
var count = Count;
var output = new T[count];
for (var i = 0; i < count; i++)
{
output[i] = this[i];
}
return output;
}
}
public T[] PeekSnapshot(int count)
{
if (count == 0) return null;
lock (_lock)
{
if (count > Size)
{
throw new ArgumentOutOfRangeException(String.Format("Queue size is {0}", Size));
}
var output = new T[count];
for (var i = 0; i < count; i++)
{
output[i] = this[i];
}
return output;
}
}
/// <summary>
/// Gets the front of the queue. The earliest item added to the queue.
/// Use this in lieu of checking this[0] as that could be null whilst items still
/// exist in the queue
/// </summary>
/// <value>
/// The front.
/// </value>
public T Front()
{
lock (_lock)
{
return IsEmpty ? _default : _buffer[_start];
}
}
/// <summary>
/// Gets the utilisation of the datastructure i.e % of the datastructure currently in use
/// </summary>
/// <value>
/// The utilisation.
/// </value>
public float Utilisation
{
get
{
lock (_lock)
{
return CalculateUtilisation();
}
}
}
private float CalculateUtilisation()
{
var util = 0f;
if (Size > 0)
{
util = (Count/(float) Size)*100;
}
return util;
}
/// <summary>
/// Returns the latest item in the queue.
/// </summary>
/// <returns></returns>
public T Back()
{
lock (_lock)
{
return Count > 0 ? _buffer[_end - 1] : _default;
;
}
}
public WeakArray<T> TakeWeakArraySnapshot()
{
lock (_lock)
{
var count = Count;
var arr = new WeakArray<T>(count);
for (var i = 0; i < count; i++)
{
arr[i] = Dequeue();
}
return arr;
}
}
/// <summary>
/// Gets the internal array. Use with EXTREME CAUTION! This is not a thread safe operation
/// and should ONLY be used in situations where any modification to the queue is not possible.
/// Modification operations include Enqueing or Dequeing
/// </summary>
/// <returns></returns>
public T[] GetInternalArray()
{
return _buffer;
}
// doing ArrayOne and ArrayTwo methods returning ArraySegment<T> as seen here:
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1957cccdcb0c4ef7d80a34a990065818d
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1f5081a54afbc2dfc1a7fb20329df7d5b
// should help a lot with the code.
#region Array items easy access.
// The array is composed by at most two non-contiguous segments,
// the next two methods allow easy access to those.
private ArraySegment<T> ArrayOne()
{
if (_start < _end)
{
return new ArraySegment<T>(_buffer, _start, _end - _start);
}
else
{
return new ArraySegment<T>(_buffer, _start, _buffer.Length - _start);
}
}
private ArraySegment<T> ArrayTwo()
{
if (_start < _end)
{
return new ArraySegment<T>(_buffer, _end, 0);
}
else
{
return new ArraySegment<T>(_buffer, 0, _end);
}
}
#endregion
}
Most data structure performance and efficiency comes down to the problem you are trying to solve.
I would recommend not to re-implement data structures yourself unless they don't exist already. Things like ngenerics exist exactly to solve those kind of problems.
This is just a sample project but I'm sure there is other types.

Drawing board game "tokens" to board and datagrid view

I'm currently working on an exercise for my c# class. I am having some trouble with one particular part and would really appreciate some help.
I am working on an exercise in which we are given an incomplete project file. The project was to create a board game which lets up to six people play (fairly simple in theory). The part I am currently stuck on is showing the player "tokens" on the board.
This is what the final board is meant to look like:
As you can see, the on "0" (start square) there are 6 circles (or tokens) and on the right side there is a datagrid view with columns showing relevant information (colour, name, money, winner).
This is what I have been able to do so far:
As you see, I have been able to show the player name, and money. Though I cannot get the colour to show up in the start square or the data grid view. Instead, I get this:
The two relevant classes are as follows:
Player.CS hold the player object code (constructors etc) (sorry for the wall of text in advance, I am unsure which parts to leave out)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Diagnostics;
namespace SharedGameClasses {
/// <summary>
/// Models a player who is currently located on a particular square
/// with a certain amount of money.
/// </summary>
public class Player {
private const int INITIAL_AMOUNT = 100;
// name of the player
private string name;
public string Name {
get {
return name;
}
set {
name = value;
}
}
// amount of money owned by player
private int money;
public int Money {
get {
return money;
}
set {
money = value;
}
}
// current square that player is on
private Square location;
public Square Location {
get {
return location;
}
set {
location = value;
}
}
// whether the player is a winner, in the current game.
private bool winner;
public bool Winner {
get {
return winner;
}
set {
winner = value;
}
}
// PlayerTokenColour and PlayerTokenImage provide colours for the players' tokens (or "pieces").
private Brush playerTokenColour;
public Brush PlayerTokenColour {
get {
return playerTokenColour;
}
set {
playerTokenColour = value;
playerTokenImage = new Bitmap(1, 1);
using (Graphics g = Graphics.FromImage(PlayerTokenImage)) {
g.FillRectangle(playerTokenColour, 0, 0, 1, 1);
}
}
}
private Image playerTokenImage;
public Image PlayerTokenImage {
get {
return playerTokenImage;
}
}
/// <summary>
/// Parameterless constructor.
/// Do not want the generic default constructor to be used
/// as there is no way to set the player's name.
/// This replaces the compiler's generic default constructor.
/// Pre: none
/// Post: ALWAYS throws an ArgumentException.
/// </summary>
/// <remarks>NOT TO BE USED!</remarks>
public Player() {
throw new ArgumentException("Parameterless constructor invalid.");
} // end Player constructor
/// <summary>
/// Constructor with initialising parameters.
/// Pre: name to be used for this player.
/// Post: this player object has all attributes initialised
/// </summary>
/// <param name="name">Name for this player</param>
public Player(String name, Square initialLocation, Brush playerToken){
Name = name;
location = initialLocation;
Money = INITIAL_AMOUNT;
PlayerTokenColour = playerToken;
//######################### Code needs to be added here ##########################################
} // end Player constructor
/// <summary>
/// Rolls the two dice to determine
/// the number of squares to move forward; and
/// moves the player's location along the board; and
/// obtains the effect of landing on their final square.
/// Pre: dice are initialised
/// Post: the player is moved along the board and the effect
/// of the location the player landed on is applied.
/// </summary>
/// <param name="d1">first die</param>
/// <param name="d2">second die</param>
public void Play(Die d1, Die d2){
var roll1 = d1.Roll();
var roll2 = d2.Roll();
int numofSquares = roll1 + roll2;
Move(numofSquares);
//######################### Code needs to be added here ##########################################
} // end Play.
/// <summary>
/// Moves player the required number of squares forward
/// Pre: the number of squares to move forward
/// Post: the player is moved along the board.
/// NOTE: Refer to Square.cs regarding the NextSquare property.
/// </summary>
/// <param name="numberOfSquares">the number of squares to move</param>
private void Move(int numberOfSquares) {
//######################### Code needs to be added here ##########################################3
} //end Move
/// <summary>
/// Increments the player's money by amount
/// Pre: amount > 0
/// Post: the player's money amount is increased.
/// </summary>
/// <param name="amount">increment amount</param>
public void Credit(int amount) {
Money = Money + amount;
} //end Credit
/// <summary>
/// Decreases the player's money by amount if
/// the player can afford it; otherwise,
/// sets the player's money to 0.
/// Pre: amount > 0
/// Post: player's money is decremented by amount if possible
/// but final amount is not below zero
/// </summary>
/// <param name="amount">decrement amount</param>
public void Debit(int amount){
const int loseamount = 25;
if (Money >= 25){
Money = Money - loseamount;
} else if (Money < 25){
Money = 0;
}
//######################### Code needs to be added here ##########################################3
} //end Debit
} //end class Player
}
And HareandTortoiseGame.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using System.ComponentModel; // for BindingList.
namespace SharedGameClasses {
/// <summary>
/// Plays a game called Hare and the Tortoise
/// </summary>
public static class HareAndTortoiseGame {
// Minimum and maximum players per game
private const int MIN_PLAYERS = 2;
public const int MAX_PLAYERS = 6;
// The dice
private static Die die1 = new Die(), die2 = new Die();
// A BindingList is like an array that can grow and shrink.
//
// Using a BindingList will make it easier to implement the GUI with a DataGridView
private static BindingList<Player> players = new BindingList<Player>();
public static BindingList<Player> Players {
get {
return players;
}
}
private static int numberOfPlayers = 6; // The value 6 is purely to avoid compiler errors.
public static int NumberOfPlayers {
get {
return numberOfPlayers;
}
set {
numberOfPlayers = value;
}
}
// Is the current game finished?
private static bool finished = false;
public static bool Finished {
get {
return finished;
}
}
/// Some default player names.
///
/// These are purely for testing purposes and when initialising the players at the start
///
/// These values are intended to be read-only. I.e. the program code should never update this array.
private static string[] defaultNames = { "One", "Two", "Three", "Four", "Five", "Six" };
// Some colours for the players' tokens (or "pieces").
private static Brush[] playerTokenColours = new Brush[MAX_PLAYERS] { Brushes.Black, Brushes.Red,
Brushes.Gold, Brushes.GreenYellow,
Brushes.Fuchsia, Brushes.White };
/// <summary>
/// Initialises each of the players and adds them to the players BindingList.
/// This method is called only once, when the game first startsfrom HareAndTortoiseForm.
///
/// Pre: none.
/// Post: all the game's players are initialised.
/// </summary>
public static void InitialiseAllThePlayers(){
//Player Playerone = new Player(defaultNames[1], Board.Squares[0]);
int i = 0;
while (i < NumberOfPlayers){
players.Add(new Player(defaultNames[i], Board.Squares[0], playerTokenColours[i]));
i++;
}
//##################### Code needs to be added here. ############################################################
} // end InitialiseAllThePlayers
/// <summary>
/// Puts all the players on the Start square.
/// Pre: none.
/// Post: the game is reset as though it is being played for the first time.
/// </summary>
public static void SetPlayersAtTheStart() {
//##################### Code needs to be added here. ############################################################
} // end SetPlayersAtTheStart
public static void PlayOneRound(){
InitialiseAllThePlayers();
}
} //end class HareAndTortoiseGame
}
Any help/tips will be appreciated, thanks! If you need any more information let me know
EDIT: Additionally, I believe these methods from another class (HareandTortoiseForm.cs) are relevant
/// <summary>
/// Constructor with initialising parameters.
/// Pre: none.
/// Post: the form is initialised, ready for the game to start.
/// </summary>
public HareAndTortoiseForm() {
InitializeComponent();
HareAndTortoiseGame.NumberOfPlayers = HareAndTortoiseGame.MAX_PLAYERS; // Max players, by default.
HareAndTortoiseGame.InitialiseAllThePlayers();
Board.SetUpBoard();
SetupTheGui();
ResetGame();
}
And also ResetGame() which is where I think I am going wrong (i think this where i need to add code)
/// <summary>
/// Resets the game, including putting all the players on the Start square.
/// This requires updating what is displayed in the GUI,
/// as well as resetting the attrtibutes of HareAndTortoiseGame .
/// This method is used by both the Reset button and
/// when a new value is chosen in the Number of Players ComboBox.
/// Pre: none.
/// Post: the form displays the game in the same state as when the program first starts
/// (except that any user names that the player has entered are not reset).
/// </summary>
private void ResetGame() {
// ########################### Code needs to be written ###############################################
}
EDIT 2:
/// <summary>
/// Tells you which SquareControl object is associated with a given square number.
/// Pre: a valid squareNumber is specified; and
/// the boardTableLayoutPanel is properly constructed.
/// Post: the SquareControl object associated with the square number is returned.
/// </summary>
/// <param name="squareNumber">The square number.</param>
/// <returns>Returns the SquareControl object associated with the square number.</returns>
private SquareControl SquareControlAt(int squareNumber) {
int rowNumber;
int columnNumber;
MapSquareNumToScreenRowAndColumn(squareNumber, out rowNumber, out columnNumber);
// Uncomment the following line once you've added the boardTableLayoutPanel to your form.
return (SquareControl) boardTableLayoutPanel.GetControlFromPosition(columnNumber, rowNumber);
// Delete the following line once you've added the boardTableLayoutPanel to your form.
// return null;
} //end SquareControlAt
I can't help you on placing the tokens on DataGridView, but I would rather change the whole board into a TableLayoutPanel. That way you can easily handle each cell separately, and tokens could be just some controls, which are set visible or not.
The Colors should be shown by simply assigning them to the relevant Cells of the DataGridView:
DGV.Rows[playerIndex].Cells[0].Style.BackColor = playerColor;
The Tokens probably ought to be drawn
either on a Bitmap you display in a PictureBox
or directly on a Panel in its Paint event.
Both options are OK. Does this token area change during the game?
Is there an indication in the project that can give you a hint..?
In both cases you will need the Graphics.FillEllipse method.

C# compiler creates ref class

I was digging through some code, when suddenly a wild bug appeared...
Ok let's start.
I created this code to represent a position in a script while parsing.
/// <summary>
/// Represents a position in script. (Used for error reporting)
/// </summary>
public sealed class Position
{
/// <summary>
/// Line count.
/// </summary>
public int Line { get; set; }
/// <summary>
/// Character count.
/// </summary>
public int Char { get; set; }
/// <summary>
/// Index data.
/// </summary>
public int Index { get; set; }
/// <summary>
/// Initialize new Position object to standard values.
/// </summary>
public Position()
{
this.Line = 1;
this.Char = 1;
this.Index = 0;
}
/// <summary>
/// Copy a Position object.
/// </summary>
/// <param name="pos"></param>
public Position(Position pos)
{
this.Line = pos.Line;
this.Char = pos.Char;
this.Index = pos.Index;
}
/// <summary>
/// Initialize new Position object to given parameters.
/// </summary>
/// <param name="p_index">The index in stream.</param>
/// <param name="p_line">The line count.</param>
/// <param name="p_char">The character count</param>
public Position(int p_index, int p_line, int p_char)
{
this.Line = p_line;
this.Char = p_char;
this.Index = p_index;
}
/// <summary>
/// Check if 2 Position objects are equal.
/// </summary>
/// <param name="p1">Left operand.</param>
/// <param name="p2">Right operand.</param>
/// <returns>Returns true, if both position objects are equal.</returns>
public static Boolean operator ==(Position p1, Position p2)
{
return
p1.Index == p2.Index &&
p1.Char == p2.Char &&
p1.Line == p2.Line;
}
/// <summary>
/// Check if 2 Position objects are not equal.
/// </summary>
/// <param name="p1">Left operand.</param>
/// <param name="p2">Right operand.</param>
/// <returns></returns>
public static Boolean operator !=(Position p1, Position p2)
{
return !(p1 == p2);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <summary>
/// Equals overload.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj is Position)
return this == (Position)obj;
return false;
}
/// <summary>
/// ToString override.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("{0} - {1} - {2}", this.Index, this.Line, this.Char);
}
}
But... When I do something like this:
var pos1 = new Position();
var pos2 = pos1;
pos2.Char = 10;
Then I change the value of pos1.Char too... I don't know this behavior from C#. The other classes behave the same.
The copy constructor did not help.
I use .NET 4.5 and VS 2012 Update 3...
Can someone tell me what causes this behavior? Or atleast how to get arround this behavior...
pos1 is a reference to the new object. By setting pos2 to pos1, you now have two references to the same object. If you want two different objects, you should do
var pos1 = new Position(); // Create a new object
var pos2 = new Position(pos1); // Use your copy constructor
pos2.Char = 10;
Your ´Position´ class is a ´reference type´
When you equal pos2 to pos1 it points to the same memory location.
Changing the property of one object will thus change the other one also as it is the same object
This isn't a bug; this is correct C# behavior. Position is a class, which is a reference type. The assignment var pos2 = pos1; doesn't copy anything, it just creates another reference to the same Position instance.
You want to call your copy constructor like this:
var pos2 = new Position(pos1);

Categories

Resources