I have
struct Park
{
...
}
and I need, to add 10 elements of this struct to List.
public List<Park> Parks = new List<Park>();
static void Main(string[] args)
{
new Program().CreatePark();
...
}
public void CreatePark()
{
for (int i = 1; i <= 10; i++)
{
Park temp = new Park();
temp.ID = i;
temp.Number = "abc";
temp.Property = false;
try
{
Parks.Add(temp);
}
catch (ArgumentException)
{
Console.WriteLine("oh no :(");
}
Console.WriteLine(temp.ToString());
}
}
And this doesn't work. Console.WriteLine(temp.ToString()); - this shows struct element, but when I want to print all elements, from Parks it's not working.
Parks.Count shows 0.
Given that you're using the statement: new Program().CreatePark(); the list of objects that you've created is not accessible after you create them. You have already thrown away the instance of the object holding the List. You have no way to access the list that you created to know what you had put in it.
The only thing the rest of your code could possibly do is create an entirely separate instance that has its own list, one that is entirely different from the one that you populated.
You'll need to hold onto the reference to the Program object if you want to inspect the list after you've populated it.
Parks is an instance variable.
CreateParks is an instance method.
Main is a static method, so you can't access the instance method or variable from it.
new Program() creates a new instance of the Program class.
Since the newly created program is not stored in a variable, it is discarded, and you can't access its Parks list after that.
To access the Parks list, you need to keep a reference to the instance of the program class like this:
Program program = new Program();
program .CreateParks();
foreach(Park park in program .Parks)
{
// Do something with the park
}
For anything more than a simple program, you will be better off having all the logic which isn't related to starting the program outside the Program class.
For example, you could create a new ParkManager class which is responsible for creating the parks and providing access to them.
Your program would look a bit like this:
public static class Program
{
static void main(string[] args)
{
ParkManager parkManager = new ParkManager();
parkManager.CreateParks();
foreach (Park park in parkManager.Parks)
{
// do something with park
}
}
}
public class ParkManager
{
// Setter is private to prevent other classes from replacing the list
// This doesn't prevent other classes from adding or removing,
// replacing or editing items in the list
public List<Park> Parks {get; private set;}
// Constructor - you could populate the list from here if appropriate
public void ParkManager()
{
Parks = new List<Park>();
}
public void CreateParks()
{
// Populate the list of parks here
}
}
Related
Hi I am new to C# and I am aware on a surface level that I cannot use non static fields inside static methods. But I have a situation that I am trying to understand conceptually.
Check out this code snippet:
class CIMConversionHelper
{
private static Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ProcessingEndTimeData _procEndTimeData = new ProcessingEndTimeData();
public static TDX2KlarfResult HandleConversion(TDXProcessItem item, string fileName)
{
TDX2KlarfResult result = new TDX2KlarfResult();
result.success = true;
XmlDocument doc = new XmlDocument();
try
{
doc.Load(fileName);
}
catch (Exception ex)
{
result.success = false;
Logger.Error(ex, "XML Parsing Error: ");
return result;
}
_procEndTimeData.ToolType = toolType;
_procEndTimeData.Lot = input.cimToolContext.LOT;
_procEndTimeData.WaferScribe = input.cimWaferContainer.waferContext.WAFER_SCRIBE;
_procEndTimeData.Processing_End_Time = input.cimToolContext.PROCESSING_END_TIME;
}
public static TDX2KlarfResult Convert(TDXProcessItem item, string fileName)
{
TDX2KlarfResult result = new TDX2KlarfResult();
result.success = true;
try
{
result = CIMConversionHelper.HandleConversion(item, fileName);
}
catch (Exception ex)
{
// Failed to Parse the xml Not good mark nonrecoverable error and return.
result.errorType = "Non-Recoverable";
result.success = false;
Logger.Error(ex, "Unknown Error: ");
return result;
}
if (result.success)
{
//DBHelper.AddProcessingEndTimeToDB();
}
return result;
}
}
This is a very abridged snippet but one that captures my question. I have created an object reference as field for ProcessingEndTimeData called _procEndTimeData.
Why then is it telling me in Visual Studio that:
"an object reference is required for the non-static field, method, or property CIMConversionHelper._procEndTimeData?
I thought I should be able to assign values to the declared object "_procEndTimeData" in the 4th line inside the static function "HandleConversion"
Can someone explain to me why this reference is not enough? and why I would then have to create yet another ProcessingEndTimeData object inside the static function HandleCOnversion?
I know I can just toggle _procEndTimeData to be static but why do I need to do this if I have already created a reference in the field level?
Think about the memory of the equipment.
When you create an object, memory is reserved for it. Imagine, that because of its properties (int, char...) it occupies 32 bytes. If you create 10 objects, you will have 320 bytes occupied in memory for your objects.
But when you use "static", those properties are created only once for the class. Not once for each object you create.
So, your 10 objects can access their own properties (int, char...) and also the static ones. But from a "static" method you cannot access the properties of an object of that class because you don't have the instance of it.
A very simple example: You have a User class with the Name property. You add a static variable of type integer: static int count. That variable will be used to record the number of users you create.
In the constructor of the user you can do count++ because the instances of the class can access to the properties of the class. But if you create a static method:
public static void DoSomething()
{
// You can show a message with the number of users
MessageBox.Show(count.ToString());
// But you CAN'T access to a "Name" because this method is not
// a method of one concrete user. It's a general method, for all users
}
If you invoke DoSomething and you are created two users, "Tom" and "Jerry", in DoSomething you don't know any user.
You have 2 choices to make this work. Not sure which one applies. My guess is the first one I will show
private readonly ProcessingEndTimeData _procEndTimeData = new ProcessingEndTimeData();
change to
static private readonly ProcessingEndTimeData _procEndTimeData = new ProcessingEndTimeData();
This says that this field is also static, ie belongs to the class, not to instances of the CIMConversionHelper class.
The alternative, which I dont think you want it to create an instance of CIMConversionHelper in that method. Which as I said is probably not what you want.
If your intent is that this class (CIMConversionHelper) is all static, ie you will never create an instance of it then mark the class itself static and the compiler will ensure that you dont accidentally create non static members. ie
static class CIMConversionHelper{....}
Why is this so?
You say you have created a reference here
private readonly ProcessingEndTimeData _procEndTimeData = new ProcessingEndTimeData();
YOu have not created it yet.
You need to understand the difference between static and instance functions and fields.
Lets have an example. We have a class Thingy. It includes a factory method that makes thingy instances and keeps a count of how many it has made
public class Thingy{
static s_thingyCount = 0;
string _froop;
public static CreateThingy(){
var thing = new Thingy();
......
s_thingyCount++;
thing._froop = "hello";
return thing;
}
public void Twang(int froop){
......
}
public int Oink(string pling){
......
_froop = pling;
}
}
we can go
var t1 = Thingy.CreateThingy();
t1.Oink("Ole");
The CreateThingy does not operate on instances of the class, it operates on the class itself. The count variable does not belong to an instance, it belongs to the class itself. Note that in the create method we have to say
thing._froop = "hello";
ie which objects _froop we want to set (the one we are in the process of making).
var t1 = Thingy.CreateThingy();
now we have an instance of Thingy we can call methods on it
t1.Oink("Ole");
Look in that method
public int Oink(string pling){
......
_froop = pling;
}
we dont say which froop to set, we are manipulating an instance of the class.
We cannot do
Thingy.Oink("xxx");
Which THingy would be updates? Nor can we do
Thingy._froop = "fff";
for the same reason
but we can do
var count = Thingy.s_thingyCount;
How does this map to your class. THis method is static. It is like CreateThingy, it does not have an instance to operate on.
public static TDX2KlarfResult HandleConversion(TDXProcessItem item, string fileName)
but you do this
_procEndTimeData.ToolType = toolType;
with this field
private readonly ProcessingEndTimeData _procEndTimeData = new ProcessingEndTimeData();
this is just like doing
_froop = "hello"
in CreateThingy
_proceEndTimeData only exists in instances on your class.
It will be create when you do
new CIMConversionHelper();
But I suspect thats not what youo want to do. So you need to make _proceEndTimeData static, just like s_thingyCount
If I'm understanding it correctly, you answered yourself: "I am aware on a surface level that I cannot use non static fields inside static methods", and then you have declared a non static variable inside your method:
private readonly ProcessingEndTimeData _procEndTimeData
It should be static, apart than readonly if you want.
The reason for this has to do with object oriented programming. To access a static method from a class you don't need to instantiate it. This is why you can't reference class-level non static variables inside a static method from that class. Hope that made it a little clearer.
I know I can just toggle _procEndTimeData to be static but why do I need to do this if I have already created a reference in the field level?
You created no such thing, your _procEndTimeData is a field in every instance of CIMConversionHelper, of which you have none.
If it helps you visualize the problem better, imagine what would happen if your expectation was reality and you had the following code:
CIMConversionHelper h1 = new(), h2 = new();
CIMConversionHelper.Convert(.....whatever....);
Would it change h1._procEndTimeData? h2._procEndTimeData? It has to pick one, right? So which one does it pick?
No. static methods can only use static fields, period.
I'm currently running into an issue where a class is seemingly creating an instance of itself or repeating its constructor causing an infinite loop.
I got a Singleton called Hotel. This currently holds a 2d array of Room (Room[,] HotelGridLayout) and a list of its guests/customers (Guests). It creates a Customer at the last line of the constructor in Hotel.
Customer Constructor:
public Customer(string _classPref, Room _entrance)
{
ClassificationPreference = _classPref; // Preference for classification (type of room)
FilePath = #"HotelPictures\Customer.png"; // String for image file path
CheckIn(_entrance);
}
CheckIn():
private void CheckIn(Room _entrance)
{
_entrance.UpdateForgroundImage(FilePath);
Room _roomAvailable = HotelForm.Instance.CheckInAtHotel(ClassificationPreference);
// some more stuff that doesn't even get reached.
}
important parts of Hotel constructor:
private HotelForm()
{
HotelGridLayout = JsonToHotelLayout.DeserializeHotelLayout(JsonString); // not the issue
Customers = new List<MovableObject>();
Cleaners = new List<MovableObject>();
MovableObject _testGuest = new Customer("5 stars", HotelGridLayout[1, 0]);
}
Here is the deal: After _entrance.UpdateForgroundImage(FilePath);, right before calling CheckInAtHotel(), it either creates a new Customer class or re-runs the constructor. It should not have anything to do with UpdateForgroundImage since this literately only assigns the Image displayed in a PictureBox (it's 1 line).
UpdateForgroundImage:
public virtual void UpdateForgroundImage(string _imageFilePath)
{
PictureBoxRoom.Image = Image.FromFile(Path.Combine("..", "..", "..", "..", _imageFilePath));
}
What is going on? Am I just blind and missing something completely obvious?
Source of issue:
I found the source of the issue: In Customer.CheckIn() I was getting the HotelForm instance via it's static property. Since that Customer was created in the constructor of HotelForm and requested the instance before it was set, it kept creating/updating instances of HotelForm; this code shows why:
public static HotelForm Instance
{
get
{
lock (_padlock)
{
if (_instance == null)
{
_instance = new HotelForm();
}
return _instance;
}
}
}
The instance didn't exist yet since the constructor wasn't finished (so to say), so it created a new instance of HotelForm, repeating the process thus constantly creating Customers.
I wanted to understand more about how the ref keyword works so I made the following experiment:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var main = new Main { Property = 1 };
var dependent = new Dependent(main);
void ChangeRef(ref Main Oldmain, Main newMain)
{
Oldmain = newMain;
}
ChangeRef(ref main, new Main { Property = 5 });
Assert.AreEqual(5,dependent.Main.Property);
}
}
public class Main
{
public int Property { get; set; }
}
public class Dependent
{
public Dependent(Main main)
{
Main = main;
}
public Main Main { get; set; }
}
As you can see I was expecting to be able to replace the object that main was referencing while keeping the reference, but the test fails and with the value still being 1. Could someone elaborate abit on why that wasnt working or point me to a place where I can read more?
Update:
Like someone answered below, but later removed.
Why doesnt it work if I pass the main object by reference to the dependent in the constructor?
Shouldnt they all have the same reference?
As others have pointed, you cannot instantly make all variables and fields in your program point to a different instance.
But if you want to reflect a change in all parts of the program, the simplest way is to wrap it in a different class (like your Dependent class). Then you can share the class with other parts of the program, and change its properties instead:
class SomeOtherObject
{
readonly Dependent _dependent;
public Dependent { get { return _dependent; }}
public SomeOtherObject(Dependent dependent)
{
_dependent = dependent;
}
public void Print()
{
Console.WriteLine(_dependent.Main.Property);
}
}
So now you can do this:
var dependent = new Dependent(new Main { Property = 1 });
var someOtherObject = new SomeOtherObject(dependent);
// this will print "1"
someOtherObject.Print();
dependent.Main = new Main { Property = 5; };
// this will print "5"
someOtherObject.Print();
In this case, obviously, simply changing dependent.Main.Property would also do the trick. So, if all parts of your program point to a single object, you can mutate it (i.e. change its internal data) and everyone will see the change, but you cannot make all parts of your program change what they are pointing to.
It's worth noting that you need to be careful when doing this in multithreaded programs; you rarely want some other thread to be able to randomly change your internal data.
That's also why it's best to try to keep your properties readonly, and your objects immutable, if possible.
After doing that
var main = new Main { Property = 1 };
You have object of type Main allocated somewhere in memory (let's name it Main1), at some memory address X, and variable main points to that object. "Points" means it literally stores address of that object Main1, so main contains X.
Then you pass reference to Main1 to the constructor of Dependent object
var dependent = new Dependent(main);
Dependent object is also allocated somewhere in memory, and one of its fields stores reference to Main1 object. So dependent.Main also stores X.
When you do
ChangeRef(ref main, new Main { Property = 5 });
You allocate new object Main5 somewhere at memory address Y. Now you change what address variable main points to. Before it stored address X (address of Main1), now it stores address Y (address of Main5). But dependent.Main still stores address X, because you didn't change it in any way, so it still points to object Main1.
I am currently working with C# using the Unity3D engine and have come upon the following problem:
I created a class that has two private references to instances of another class which it has to access. Once I create multiple instances of the class and set the references I found out that all instances were using the same variable. I realized this as I was destroying an instance and just before that set the two variables holding the references to null. Immediately after doing that all other instances were throwing NullReferenceExceptions because they were still trying to access the references. The referenced objects are fine, other scripts can still access them.
Here is some pseudo code illustrating the structure:
public class Character
{
// Character data
}
public class StatusEffect
{
private Character target;
private Character originator;
public void Init(Character _Target, Character _Originator)
{
target = _Target;
originator = _Originator;
}
public void Destroy()
{
target = null;
originator = null;
}
}
In the program it would be called like this:
StatusEffect effect = new StatusEffect();
effect.Init(player1, player2);
// Time goes by
effect.Destroy();
After calling Destroy() every StatusEffect's two references will be null.
This is not only an issue when destroying StatusEffects, but also when creating new ones. As soon as I touch the references from within a new instance all StatusEffects will reference the two Characters specified by the new StatusEffect.
I do not understand why or how I can fix this issue. Can someone enlighten me on this matter?
Cheers,
Valtaroth
EDIT:
Here is the real code as requested:
I have a container class holding several StatusEffects. As soon as it starts, it initializes all of them.
public class CElementTag
{
// ..Other data..
public float f_Duration; // Set in the editor
private CGladiator gl_target;
private CGladiator gl_originator;
private float f_currentDuration;
public CStatusEffect[] ar_statusEffects;
// Starts the effect of the element tag
public void StartEffect(CGladiator _Originator, CGladiator _Target)
{
gl_originator = _Originator;
gl_target = _Target;
f_currentDuration = f_Duration;
for(int i = 0; i < ar_statusEffects.Length; i++)
ar_statusEffects[i].Initialize(gl_originator, gl_target);
}
// Ends the effect of the element tag
public void EndEffect()
{
for(int i = 0; i < ar_statusEffects.Length; i++)
{
if(ar_statusEffects[i] != null)
ar_statusEffects[i].Destroy();
}
}
// Called every update, returns true if the tag can be destroyed
public bool ActivateEffect()
{
f_currentDuration -= Time.deltaTime;
if(f_currentDuration <= 0.0f)
{
EndEffect();
return true;
}
for(int i = 0; i < ar_statusEffects.Length; i++)
{
if(ar_statusEffects[i] != null && ar_statusEffects[i].Update())
RemoveStatusEffect(i);
}
return false;
}
// Removes expired status effects
private void RemoveStatusEffect(int _Index)
{
// Call destroy method
ar_statusEffects[_Index].Destroy();
// Remove effect from array
for(int i = _Index; i < ar_statusEffects.Length - 1; i++)
ar_statusEffects[i] = ar_statusEffects[i+1];
ar_statusEffects[ar_statusEffects.Length - 1] = null;
}
}
The actual StatusEffect class is holding the two references as well as some other data it needs to work. It has virtual methods because there are some classes inheriting from it.
public class CStatusEffect
{
// ..Necessary data..
// References
protected CGladiator gl_target;
protected CGladiator gl_originator;
virtual public void Initialize(CGladiator _Target, CGladiator _Originator)
{
gl_target = _Target;
gl_originator = _Originator;
// ..Initialize other necessary stuff..
}
virtual public void Destroy()
{
gl_target = null;
gl_originator = null;
// ..Tidy up other data..
}
virtual public bool Update()
{
// ..Modifying data of gl_target and gl_originator..
// Returns true as soon as the effect is supposed to end.
}
}
That should be all the relevant code concerning this problem.
EDIT2
#KeithPayne I have a static array of ElementTags defined in the editor and saved to xml. At the beginning of the program the static array is loading the xml and stores all element tags. When creating a new element tag to use I utilize this constructor:
// Receives a static tag as parameter
public CElementTag(CElementTag _Tag)
{
i_ID = _Tag.i_ID;
str_Name = _Tag.str_Name;
enum_Type = _Tag.enum_Type;
f_Duration = _Tag.f_Duration;
ar_statusEffects = new CStatusEffect[_Tag.ar_statusEffects.Length];
Array.Copy(_Tag.ar_statusEffects, ar_statusEffects, _Tag.ar_statusEffects.Length);
}
Do I have to use a different method to copy the array to the new tag? I thought Array.Copy would make a deep copy of the source array and stored it in the destination array. If it is in fact making a shallow copy, I understand where the problem is coming from now.
From Array.Copy Method (Array, Array, Int32):
If sourceArray and destinationArray are both reference-type arrays or
are both arrays of type Object, a shallow copy is performed. A shallow
copy of an Array is a new Array containing references to the same
elements as the original Array. The elements themselves or anything
referenced by the elements are not copied. In contrast, a deep copy of
an Array copies the elements and everything directly or indirectly
referenced by the elements.
Consider this fluent version of the StatusEffect class and its usage below:
public class StatusEffect
{
public Character Target { get; private set; }
public Character Originator { get; private set; }
public StatusEffect Init(Character target, Character originator)
{
Target = target.Clone()
Originator = originator.Clone();
return this;
}
//...
}
public CElementTag(CElementTag _Tag)
{
i_ID = _Tag.i_ID;
str_Name = _Tag.str_Name;
enum_Type = _Tag.enum_Type;
f_Duration = _Tag.f_Duration;
ar_statusEffects = _Tag.ar_statusEffects.Select(eff =>
new StatusEffect().Init(eff.Target, eff.Originator)).ToArray();
// ar_statusEffects = new CStatusEffect[_Tag.ar_statusEffects.Length];
// Array.Copy(_Tag.ar_statusEffects, ar_statusEffects, _Tag.ar_statusEffects.Length);
}
Because you're passing in references to the objects via your Init() method, you're not actually "copying" the objects, just maintaining a reference to the same underlying objects in memory.
If you have multiple players with the same references to the same underlying objects, then changes made by player 1 will effect the objects being used by player 2.
Having said all that, you're not actually disposing the objects in your Destory method. Just setting the local instance references to Null which shouldn't affect any other instances of StatusEffects. Are you sure something else isn't disposing the objects, or that you haven't properly init'd your other instances.
If you do want to take a full copy of the passed in objects, take a look at the ICloneable interface. It looks like you want to pass in a copy of the objects into each Player.
public class Character : ICloneable
{
// Character data
//Implement Clone Method
}
public class StatusEffect
{
private Character target;
private Character originator;
public void Init(Character _Target, Character _Originator)
{
target = _Target.Clone()
originator = _Originator.Clone();
}
The fields aren't shared(static) among other instances. So calling target = null; in Destroy() won't affect other instances.
StatusEffect effect1 = new StatusEffect();
effect1.Init(player1, player2);
StatusEffect effect2 = new StatusEffect();
effect2.Init(player1, player2);
// Time goes by
effect2.Destroy();
// Some more time goes by
// accessing effect1.target won't give a `NullReferenceException` here unless player1 was null before passed to the init.
effect1.Destroy();
I think you did forget the Init(..) on the other instances. Every time you create an instance of StatusEffect, you need to call Init(...).
Update:
This line will clear the reference to the effect, but you never recreate it:
ar_statusEffects[ar_statusEffects.Length - 1] = null;
so the next time you call ar_statusEffects[x].Update() or Initialize() etc it will throw a NullReferenceException
If you want to clear out effects within you array, you could create an Enable bool in the effect, this way you only have to set/reset it.
for(int i = 0; i < ar_statusEffects.Length; i++)
if(ar_statusEffects[i].IsEnabled)
ar_statusEffects[i].Update();
Why don't you use a List instead? Arrays will be faster as long you don't have to shuffle in it. (like circulair buffers etc)
Thanks to Keith Payne I figured out where the problem was. I was creating a deep copy of CElementTag, but not of my ar_statusEffects array. I wrongly assumed Array.Copy was creating a deep copy of an array when it actually was not.
I implemented the IClonable interface for my CStatusEffect and use the Clone() method to create a true deep copy for each member of the static array and add it to the new tags ar_statusEffects array. This way I have seperate instances of the effects instead of references to the same static effect.
Thanks to everyone, especially Keith Payne, for their help and support!
I am attempting following code to create multiple instances of a class at run-time and want to initialize also, but it is giving error:
A local variable named 'inum' cannot be declared in this scope because
it would give a different meaning to 'inum', which is already used in
a 'parent or current' scope to denote something else.
public class MyClass
{
static int i=0;
class A
{
public A()
{
}
}
public static void Run()
{
string inum = "i";
for (int j=1;j<=5;j++)
{
inum = inum + j.ToString();
//Initialize Instance
A inum = new A();
}
}
}
You appear to be trying to use variable names "dynamically". That doesn't work in C#, and you should change how you think about variables. If you want to create several instances, declare an array:
public class MyClass
{
static A[] instances;
class A
{
public A()
{
}
}
public static void Run()
{
instances = new A[5];
for (int j=0;j<5;j++)
{
instances[j] = new A();
}
}
}
You cannot have dynamic variable in c#. The append you are trying is appending the value not the variable pointer.
rather use this way
Dictionary<int, A> inum = new Dictionary<int, A>();
for (int j=1;j<=5;j++)
{
//Initialize Instance and add to dictionary
inum.Add(j, new A());
}
You can get them by key name. There are several other way to store instances as collection
I'm not a C# programmer by any stretch of the imagination, but by the rules of Java and any other similarly syntaxed language I know anything about, what you are doing is attempting to redeclare 'inum' with a new type, after it has been declared as a string in the same scope.
The other point is that even if this were not the case, you are not creating multiple instances but filling the same variable with a new instance 5 times, which would only result in one instance (the last one).
From quickly reading a C# tutorial I think this is something like what you want. I'm not sure what you were trying to do with the 'inum' variable so it is gone, as is static variable 'i':
public class MyClass
{
class A
{
public A()
{
}
}
public static void Run()
{
// Declare array to hold instances
A[] instances;
// instances is now five elements long
instances = new A[5];
for (int j=0;j<5;j++)
{
//Initialize Instance
instances[j] = new A();
}
}
}
That should result in an array of 5 objects called 'instances' in the scope of the Run method - you may want this in the scope of the class itself, possibly as a static property.
As a side note, it's good practice to start at 0, not 1, for operations like this (with the var 'j') and the above code reflects this.
you cannot call the variable of type A "inum" (there exists already one called like that)
you have to give it another name like:
A anyOtherName = new A();
try to name the variable A with different name
A objA = new A();