I'm new to C# - this is nearly my first program. I'm trying to create some public static variables and constants to use anywhere in the program. The - wrong - way I have tried is to declare them in a separate class in the same namespace but they are out of context for the main program. It's a WPF application. The code looks like this:
namespace testXyz
{
class PublicVars
{
public const int BuffOneLength = 10000;
public static int[] Buff1 = new int[BuffOneLength];
public const int BuffTwoLength = 2500;
public static int[] Buff2 = new int[BuffTwoLength];
private void fillBuff1()
{
Buff1[0] = 8;
Buff1[1] = 3;
//etc
}
private void fillBuff2()
{
Buff2[0] = 5;
Buff2[1] = 7;
//etc
}
}
}
Second file:
namespace testXyz
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static int isInContext = 0;
int jjj = 0, mmm = 0;
private void doSomething()
{
isInContext = 5; // this compiles
if (jjj < BuffOneLength) // "the name 'BuffOneLength' does not exist in the current context"
{
mmm = Buff2[0]; // "the name 'Buff2' does not exist in the current context"
}
}
}
}
My actual program is much longer of course. I created the above WPF application exactly as shown to test this problem and I got these errors, also occurring in the real program. I really don't want to fill the arrays in the main program as they are very long and it would mean much scrolling. I also want to have one place where I can declare certain public static variables. What is the right way to do this?
You have to either specify class:
// BuffOneLength from PublicVars class
if (jjj < PublicVars.BuffOneLength) {
...
// Buff2 from PublicVars class
mmm = PublicVars.Buff2[0];
or put using static:
// When class is not specified, try PublicVars class
using static testXyz.PublicVars;
namespace testXyz {
public partial class MainWindow : Window {
...
// BuffOneLength - class is not specified, PublicVars will be tried
if (jjj < BuffOneLength) {
mmm = Buff2[0];
You can't access a static variable that is in another class by just calling the variable. You need to first go thru the class that contains it in your case it would be
PublicVars.BuffOneLength
and
PublicVars.Buff2[0]
Related
A simple/beginner question on code resuse in a basic OOP program recording the sale of motor vehicle tyres.
The tyres are stored in an array:
public Tyre[] tyres = new Tyre[5];
I have two forms.
Form1
End user simply uses a combo/lookup of current stock items (tyres) to select item.
public partial class Form1 : Form
{
Fitting fitting;
public Tyre[] tyres = new Tyre[5];
Tyre currentTyre;
public Form1()
{
InitializeComponent();
//populate array with tyres
tyres[0] = new Tyre("155/80S13", 56.00m, 10);
tyres[1] = new Tyre("165/70P15", 42.00m, 10);
tyres[2] = new Tyre("195/70S13", 46.00m, 10);
tyres[3] = new Tyre("158/90S19", 70.00m, 10);
tyres[4] = new Tyre("185/66R13", 66.00m, 10);
}
// search through array to find current selected tyre
public Tyre findTyre(string tyretype)
{
for (int i = 0; i < tyres.Length; i++)
{
if (tyretype == tyres[i].Type)
return tyres[i];
}
return null;
}
private void cmbTyreType_SelectedIndexChanged(object sender, EventArgs e)
{
currentTyre = findTyre(cmbTyreType.Text);
lblPrice.Text = currentTyre.Price.ToString();
lblStockQ.Text = currentTyre.StockQty.ToString();
}
The findTyre() method is defined as:
public Tyre findTyre(string tyretype)
{
for (int i = 0; i < tyres.Length; i++)
{
if (tyretype == tyres[i].Type)
return tyres[i];
}
return null;
}
Form 2 (AddStock) On this form, the end user currently uses a similar combo/lookup again to view the range of tyres (just like on Form1)
public partial class AddStock : Form
{
Form1 frm2;
Tyre currentTyre;
public AddStock(Form1 frm)
{
frm2 = frm;
InitializeComponent();
for (int i = 0; i< frm.tyres.Length ; i++)
{
cmbTyreType.Items.Add(frm.tyres[i].Type);
}
}
public Tyre findTyre(string tyretype )
{
for (int i = 0; i < frm2.tyres.Length; i++)
{
if (tyretype == frm2.tyres[i].Type)
return frm2.tyres[i];
}
return null;
}
private void cmbTyreType_SelectedIndexChanged(object sender, EventArgs e)
{
currentTyre = findTyre(cmbTyreType.Text);
lblCurrentQ.Text = currentTyre.StockQty.ToString();
}
My concern is that I've had to re-define findTyre() again, eventhough it was already defined in Form1. My hope was (perhaps ill-informed) that i could re-use the findTyre() method from Form1, but Visual Studio is preventing me.
Could the reason be that the findTyre() method is bound to instances which render it inaccessible outside the class?
Create a class that manages your tyres:
public class Tyres
{
private Tyre[] tyres = new Tyre[5];
public Tyre this[int i]
{
get { return tyres[i]; }
set { tyres[i] = value; }
}
public Tyre findTyre(string tyretype )
{
for (int i = 0; i < frm2.tyres.Length; i++)
{
if (tyretype == frm2.tyres[i].Type)
return frm2.tyres[i];
}
return null;
}
}
Instead of passing the array between forms, pass an instance of this class. This class can also hold even more specific methods, which do operations on your tyres. This is one fundamental of OOP: keeping the data and the methods acting on that data togehter (in a class). This also helps you separating your logic from your GUI. Imagine you would like to change your winforms application to a console or a web application. In that case, you can reuse the tyres class. Searching for a tyre has nothing to do with a GUI, that's why it doesn't belong into a form class.
Why is the findTyre method inside the forms class and not inside the Tyres class?
In your case i would just move the method inside the Tyre object class make it static and add a new parameter to the method.
It is never good to have duplicate methods inside your program. Reason being if that method changes functionaly you will have to change it everywhere in your whole program instead of one. Also always try to bind you object specific code to your object class. So you know if you need a method that is linked to tyres you just need to look into the tyres class and not go through all your classes trying to find it.
I would recommend you also read the following article, it is an explanation to what separation of concerns is: https://www.castsoftware.com/blog/how-to-implement-design-pattern-separation-of-concerns
public static Tyre findTyre(string tyretype, Tyres[] Tyres )
for (int i = 0; i < tyres.Length; i++)
{
if (tyretype == tyres[i].Type)
return tyres[i];
}
return null;
}
I would also recommend you implementering the code #SomeBody provided in the answer below. Will make your code cleaner and more sustainable.
In AddStock can't you just use the findTyre method from Form1?
currentTyre = frm2.findTyre(cmbTyreType.Text);
Seems you can just call findTyre from Form1
public Tyre findTyre(string tyretype)
=> frm2.findTyre(tyretype)
I have two classes, one for defining the algorithm parameters and another to implement the algorithm:
Class 1 (algorithm parameters):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VM_Placement
{
public static class AlgorithmParameters
{
public static int pop_size = 100;
public static double crossover_rate = 0.7;
public static double mutation_rate = 0.001;
public static int chromo_length = 300;
public static int gene_length = 4;
public static int max_allowable_generations = 400;
static Random rand = new Random();
public static double random_num = rand.NextDouble();
}
}
Class 2 (implement algorithm):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VM_Placement
{
public class Program
{
public struct chromo_typ
{
public string bits;
public float fitness;
//public chromo_typ(){
// bits = "";
// fitness = 0.0f;
//}
chromo_typ(string bts, float ftns)
{
bits = bts;
fitness = ftns;
}
};
public static int GetRandomSeed()
{
byte[] bytes = new byte[4];
System.Security.Cryptography.RNGCryptoServiceProvider rng =
new System.Security.Cryptography.RNGCryptoServiceProvider();
rng.GetBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
public string GetRandomBits()
{
string bits="";
for (int i = 0; i < VM_Placement.AlgorithmParameters.chromo_length; i++)
{
if (VM_Placement.AlgorithmParameters.random_num > 0.5f)
bits += "1";
else
bits += "0";
}
return bits;
}
public static void Main(string[] args)
{
Random rnd = new Random(GetRandomSeed());
while (true)
{
chromo_typ[] Population = new chromo_typ[VM_Placement.AlgorithmParameters.pop_size];
double Target;
Console.WriteLine("\n Input a target number");
Target = Convert.ToDouble(Console.ReadLine());
for (int i = 0; i < VM_Placement.AlgorithmParameters.pop_size; i++)
{
Population[i].bits = GetRandomBits();
Population[i].fitness = 0.0f;
}
}
}
}
}
I am getting an error on Population[i].bits = GetRandomBits(); in Main().
Error is:
An object reference is required for the non-static field, method, or property 'VM_Placement.Program.GetRandomBits()'
Am I missing anything?
The Main method is Static. You can not invoke a non-static method from a static method.
GetRandomBits()
is not a static method. Either you have to create an instance of Program
Program p = new Program();
p.GetRandomBits();
or make
GetRandomBits() static.
It looks like you want:
public static string GetRandomBits()
Without static, you would need an object before you can call the GetRandomBits() method. However, since the implementation of GetRandomBits() does not depend on the state of any Program object, it's best to declare it static.
The Main method is static inside the Program class. You can't call an instance method from inside a static method, which is why you're getting the error.
To fix it you just need to make your GetRandomBits() method static as well.
I have a class with a PictureBox created as followed:
public class Tile
{
public PictureBox tilePB = new PictureBox(); //properties don't matter in this case
}
I also have a class GameManager. This is like a referee.
I want to make it so the BackColor of Tile.tilePB can only be edited by Gamemanager and nothing else, and no other class.
I currently have a public PictureBox for Gamemanager (to edit) and a public get function for other classes, but I want to actually make this a valid system instead of what I have right now.
Is this even possible? Please include explenation for the required code.
EDIT: I ran into an issue that I hadn't thought off: class Gamemanager is a static class. I do everything in that class via public static functions. Is this still possible? Since this doesn't work.
You can't do this at compile time, but it can be done at runtime:
public class PictureBox
{
private Color _backColor;
public void SetBackColor(Color color)
{
//getting class type that called this method
var stackTrace = new StackTrace();
var stackFrames = stackTrace.GetFrames();
var callingFrame = stackFrames[1];
var method = callingFrame.GetMethod();
//checking if the class type is GameManager
if (!method.DeclaringType.IsAssignableFrom(typeof(GameManager)))
{
throw new FieldAccessException("Only GameManager can set the background color of a PictureBox!");
}
_backColor = color;
}
public Color BackColor => _backColor;
}
public class Tile
{
public PictureBox tilePB { get; set; }
}
//example GameManager class
public class GameManager
{
public void SetBackground()
{
var someTile = new Tile()
{
tilePB = new PictureBox()
};
var someColor = new Color();
someTile.tilePB.SetBackColor(someColor);
}
}
//example class that may want to set picturebox background color
public class MaliciousClass
{
public void SetBackground()
{
var someTile = new Tile()
{
tilePB = new PictureBox()
};
var someColor = new Color();
someTile.tilePB.SetBackColor(someColor);
}
}
Then somewhere:
var gm = new GameManager();
var mc = new MaliciousClass();
gm.SetBackground(); //this is fine
mc.SetBackground(); //this will throw an exception
If you don't want to throw an exception or you want to do something different when "not authorized" class is trying to access the SetBackColor method then just replace throw new FieldAccessException() with return or whatever you want.
Bare in mind the approach presented here is inefficent and it just presents that in can be done at runtime and nothing more than that.
Not sure if this is exactly what you are looking for, but I made this quick test and it seems to be able to differentiate the calling class:
class Program
{
static void Main(string[] args)
{
Type1 something1 = new Type1();
Type2 something2 = new Type2();
something1.runTest();
something2.runTest();
Console.ReadKey();
}
public class Type1
{
public void runTest()
{
Testing.edit(this);
}
}
public class Type2
{
public void runTest()
{
Testing.edit(this);
}
}
public static class Testing
{
public static void edit(object obj)
{
// This is where you test the calling class to make sure
// it is allowed to edit.
Console.WriteLine(obj.GetType().ToString());
}
}
}
The only way I can think of where you enforce this at compile time, end up being a bit complicated. I don't think you'll want to do this.
You can create an interface with properties/methods for everything that only the GameManager is allowed to do. You can implement this interface in a private inner class below Tile, and make sure the only way this object is created is by passing in a GameManager that receives it. Now, the only way the access can 'leak' is if the GameManager 'gives away' the object.
public class GameManager {
public void AddTile(Tile t, Tile.IManagerHook m) {
m.SomeProperty = "set from manager";
}
}
public class Tile
{
public object SomeProperty { get; private set; }
public Tile(GameManager manager) {
manager.AddTile(this, new ManagerHook(this));
}
public interface IManagerHook {
object SomeProperty {get; set;}
}
private class ManagerHook : IManagerHook {
private Tile _tile;
public ManagerHook(Tile t) {
_tile = t;
}
public object SomeProperty {
get{ return _tile.SomeProperty;}
set { _tile.SomeProperty = value; }
}
}
}
(seems) Simply not possible
After asking several programmers, the way I have coded everything and what I want seems to be simply impossible without immensely complicated code - to the point you are better off refacturing everything. Since class Gamemanager is a static class, there will be no instances of it so you can not check if the 'object' that called it is of class Gamemanager. this also doesn't work since Gamemanager is, agian, static.
This is code inside Proizvod.cs file
namespace mateo_zadatak
{
class Proizvod
{
public string Šifra = "šifra";
public string Naziv = "naziv";
public string Proizvođač = "proizvođač";
public float Cijena;
public int Količina;
private float Ukupno;
private int Popust;
private float UkupnoPopust;
private void variable()
{
Ukupno = Cijena * Količina;
{
if (Količina < 10)
{
Popust = 0;
}
else if (Količina > 9 && Količina < 31)
{
Popust = 5;
}
else Popust = 10;
}
}
}
}
I have to use this variables in Form1.cs file , because i will need some calculations from datagrid. How to connect those two files?
There are no "Global" variables in C# but what you can do is create a new Class with Static variables and assign/use them. For example:
public static class GlobalVariables
{
public static int IntVariable;
public static string StringVariable;
}
And then you can reference them as GlobalVariables.IntVariable and GlobalVariables.StringVariable.
A Form is just a class - Add public properties/members to the Form and access them from a reference to the form.
Make your class public, and make sure you include Proizvod.cs file into your project or add a reference to it from your project.
You can use public properties instead of variables from another class
Perhaps I'm missing something, but:
public class Form1 : ...
{
public Proizvod Foo;
Form1()
{
Foo = new Proizvod();
}
SomeMethod()
{
MessageBox.Show(Foo.Naziv);
}
}
I've created a program which should calculate the surface area of an irregular shaped object, such as a lake.
I read in a file, which contained the values for the x and y values, and the depth.
I'm new to C#, and so I don't fully understand everything yet, but I think my code should work, however, it doesn't seem to be writing the value for the area onto the screen.
I know that Console.WriteLine(_surface); SHOULD work , but it I can't seem to get it to do anything, and it's probably in the wrong place!
Can someone please tell me where I'm going wrong?
My code is as follows.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NUnit.Framework;
namespace ConsoleApplication1
{
public class ValueXyz
{
public double X { get; set; }
public double Y { get; set; }
public int Z { get; set; }
}
public class SurfaceCalculator
{
private ValueXyz[] _valuesXyz;
private double _surface;
private readonly string _textWithValues;
public SurfaceCalculator(string textWithValues)
{
_textWithValues = textWithValues;
SetValuesToCalculate();
}
public double Surface
{
get { return _surface; }
}
public void CalculateSurface()
{
for (var i = 0; i < _valuesXyz.Length; i++)
{
if (_valuesXyz[i].Z == 0)
_surface = (_valuesXyz[i].X * _valuesXyz[i + 1].Y) - (_valuesXyz[i + 1].X * _valuesXyz[i].Y);
Console.WriteLine(_surface);
}
}
private void SetValuesToCalculate()
{
var valuesXyz = _textWithValues.Split(' ');
_valuesXyz = valuesXyz.Select(item => new ValueXyz
{
X = Convert.ToDouble(item.Split(',')[0]),
Y = Convert.ToDouble(item.Split(',')[1]),
Z = Convert.ToInt32(item.Split(',')[2])
}).ToArray();
}
public void TestSurfaceCalculatorGetsAValue()
{
var textWithValues = File.ReadAllLines(#"C:\Users\user\Documents\Visual Studio 2010\Projects\Lake_Take_Toooooo\Lake_Take_Toooooo\bin\Debug\Lake_Test.csv");
var calculator = new SurfaceCalculator(_textWithValues);
calculator.CalculateSurface();
Assert.IsNotNull(calculator.Surface);
}
static void Main()
{
Console.ReadKey();
}
}
}
This is my first time using classes, so apologies if there's an obvious answer.
Thanks for your help!
You need to actually call the method inside the Main method, which is the program entry point. Like:
static void Main()
{
CalculateSurface();
Console.ReadKey();
}
When you run your program, only the code inside the Main method is actually executed. If you do not call anything from there then no code is executed.
No function is being called in the Main event...
I should imagine read key will wait for key input then close, correct?
Are you trying to run this as console application or as unit test? (It looks like you're trying to run it as unit test since you're using NUnit.Framework and there is a Test-method with an Assert...)
If you want to run it as console application, you have to call the code that should get executed in the Main method.
If you want to run it as unit test, you have to add some "attributes" to your test class and test method. The class should have the [TestFixture] attribute, and the method should have the [Test] attribute, like:
[TestFixture]
public class SurfaceCalculator {
...
[Test]
public void TestSurfaceCalculatorGetsAValue() {
...
}
}