Better understanding scope - c#

I'm still very new to C#, but I thought I understood the concept of scope. I'm having a problem with a program and I would really appreciate some help.
The problem with the following code is that Line 35 fails with
"An object reference is required for the non-static field, method, or property".
You can see that object Mail is instantiated as part of the Program class and it seems like it should be globally accessible. But when I try to use Mail.Add in the InitMail() method, it doesn't recognize the Mail object.
If I move the instantiation and InitMail code into Main(), it works just fine (although I also have to remove public modifier on the instantiation). What am I not understanding here?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestApp1103
{
class Program
{
// Define an enum type named "Division" specifying all possible values:
public enum Division {PFR, PSE, PVF, PVM, PVS}
//Define a generic class named "MailList" and specify accessor methods:
public class MailList
{
public Division Div { get; set;}
public string[] SuccAddr { get; set; }
public string[] FailAddr { get; set; }
}
// Instantiate a MailList object named "Mail":
public List<MailList> Mail = new List<MailList>();
static void Main(string[] args)
{
// Populate the object "Mail":
InitMail();
}
static void InitMail()
{
Mail.Add( new MailList()
{
Div = Division.PFR,
SuccAddr = new string[2] { "addr1#contoso.com", "addr2#contoso.com" },
FailAddr = new string[2] { "addr3#contoso.com", "addr4#contoso.com" }
});
}
}
}

static void InitMail() {
Mail.Add( new MailList() {
// properties
});
}
This will try to add a new MailList object to Mail.
However when we look at Mail, we see this declaration:
public List<MailList> Mail = new List<MailList>();
Notice the absence of static which is present in InitMail().
This means that when the method InitMail() would be executed statically (Program.InitMail()), it would try to access the non-static variable Mail.
Thus the compiler complains.

Mail is an instance field - not a static one.
That means it belongs to instances of the class it is declared on - which there are none.
There are a couple of ways to go about fixing the issue:
Make the field static.
Instantiate Program and call InitMail on the variable.

You are trying to access the instance variable Mail from a static method.
This can not work as you need an object instance of your class Program to access the instance variable

Related

Why VS suggests me to add this class reference as static when it isn't?

I was wondering why IntelliSense in Visual Studio 2017 v15.9.5 is actually suggesting me adding this class reference as static since:
Hasn't got any private constructor
Hasn't got any static methods
Hasn't got any static members
On the class which is referencing this no-static class I'm just accessing the public enum ("Modo")
Class's code:
using System.Collections.Generic;
namespace Formularios
{
public class Tipos
{
public enum GridControlMenusEdicion
{
Predeterminado,
Siempre,
Nunca
}
public enum Modo
{
Nuevo,
Modificacion,
Consulta
}
public enum TipoCampo
{
Texto,
Fecha,
Numero,
SiNo
}
public enum EstadoEntidad
{
Nueva,
Modificacion
}
public enum RangoDatos
{
Hoy,
Ayer,
EstaSemana,
SemanaPasada,
EsteMes,
MesPasado,
Ultimos3Meses,
AnoEncurso
}
}
class RangoDatosProvider
{
public Dictionary<Tipos.RangoDatos, string> DiccionarioRangoDatos()
{
return new Dictionary<Tipos.RangoDatos, string>
{
{ Tipos.RangoDatos.Hoy, "Hoy" },
{ Tipos.RangoDatos.Ayer, "Ayer" },
{ Tipos.RangoDatos.EstaSemana, "Esta semana" },
{ Tipos.RangoDatos.SemanaPasada, "Semana pasada" },
{ Tipos.RangoDatos.EsteMes, "Este mes" },
{ Tipos.RangoDatos.MesPasado, "Mes pasado" },
{ Tipos.RangoDatos.Ultimos3Meses, "Últimos 3 meses" },
{ Tipos.RangoDatos.AnoEncurso, "Año en curso" }
};
}
public string GetTextoLoadingRangoDatos(Tipos.RangoDatos seleccionUser)
{
switch (seleccionUser)
{
case Tipos.RangoDatos.SemanaPasada:
return "la " + DiccionarioRangoDatos()[seleccionUser].ToLower();
case Tipos.RangoDatos.Ultimos3Meses:
return "los " + DiccionarioRangoDatos()[seleccionUser].ToLower();
case Tipos.RangoDatos.AnoEncurso:
return "este año";
default:
return DiccionarioRangoDatos()[seleccionUser].ToLower();
}
}
}
}
Class which is referencing the previous code (used on the constructor):
using Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Formularios.Configuracion
{
public partial class frmConfiguracionPerfilEmail : frmBase
{
private readonly PerfilEmailGestion _perfilEmailGestion = new PerfilEmailGestion();
private readonly HashSet<string> _aliasExistentes;
public frmConfiguracionPerfilEmail(PerfilEmail mailSettings, HashSet<string> aliasExistentes, Modo modo)
{
InitializeComponent();
Inicializar(mailSettings);
_aliasExistentes = aliasExistentes;
Modo = modo;
}
}
}
If I call IntelliSense for fast-adding references/usings it suggests me two options:
a) "using static Formularios.Tipos"
b) "Modo" as constructor parameter would become "Tipos.Modo"
Choosing a) would lead to adding a static reference which I don't get why.
Choosing b) would lead to adding the full namespace reference. I personally always try to remove all type qualifiers to simplify reading the code.
Any light thrown into clarifying this will be quite welcome.
P.S: Sorry for not translating the code, but I wanted to focus on the "why" and not the content itself. The inherited class is not referencing the "no-static" Formularios.Tipos class.
First off, the suggestion is not to add a "class reference as static", but rather a "static import".
Modo is not available to your code they way you're referencing it in your frmConfiguracionPerfilEmail constructor. It's nested within the Tipos class, so it's not available as-is.
Suggestion 1 makes all static members and nested types of the Tipos class usable without having to qualify them, e.g. Modo instead of Tipos.Modo.
Suggestion 2 will not, as you said, qualify it with the full namespace. Formularios is a namespace; Tipos is a class. It's only suggesting qualification by the class name. It doesn't need the namespace because the frmConfiguracionPerfilEmail class where it's being used is already under the Formularios namespace (Formularios.Configuration) so Tipos can be referenced without namespace qualification.
If you really want to simplify reading the code, as you say, then start by un-nesting those enums. Nested types have their uses but using a class as nothing more than a container for other types is excessive. Namespaces are there for that purpose. Put the enums directly under the Formularios namespace. Or, put them in a Formularios.Tipos namespace if you want them grouped somehow.
namespace Formularios.Tipos
{
// enums
}
And if you do that, make sure you do this:
using Formularios.Tipos;
This would be the equivalent of doing using static Formularios.Tipos; with your current setup.
Your "RangoDatosProvider" class has no reason to be an instance class; it only operates on parameters and enums, meaning there is nothing in the class that can change state.
i do not know which code analysis ruleset you are using, but several hold the reasoning that "if it doesn't need to be instance, it is better off being static".
whether you agree with that is up to you, of course, but this is why it is recommended to you by visual studio

Dot notation for class member access

I've got the following code:
public class Random
{
public string Name { get; set; }
public bool IsRunning()
{
var running = true;
return running;
}
}
public class Main
{
Random newObject = new Random();
newObject.Name = "Johnny";
var result = newObject.IsRunning();
}
which all exists in the same .cs file in the same namespace. Any time I've created a new project before I've never had to set up anything to use dot notation to access member attributes or methods, but this is saying that I can't use newObject. ANYTHING, and also that "var" is not valid for a keyword. It's a windows forms application like I normally use, but I'm drawing blanks here as why I can't do all these things that I normally use many times in my other programs. What am I missing here?
You're trying to write code directly within the class declaration. A class declaration can only directly contain member declarations. It can't contain arbitrary statements such as newObject.Name = "Johnny" nor can it use var, which is only applicable to local variables. If you put the code in a method, it should be absolutely fine. For example:
public class Main
{
public void DoSomething()
{
Random newObject = new Random();
newObject.Name = "Johnny";
var result = newObject.IsRunning();
}
}
As an aside, I'd strongly recommend against naming your own class Random given that that's also the name of a class within the System namespace.
You cannot use var or assign values to some other object within a class member definition.
You code in public class Main is not within a method.
I guess what you were trying to do is writing a Console app and that needs a
public static void Main()
method
so change your class to e.g.
class Program
{
static void Main(string[] args)
{
Random newObject = new Random();
newObject.Name = "Johnny";
var result = newObject.IsRunning();
}
}

Keep getting the CS0122 Error and do not understand how to fix it

I have this code, and the 'lblPatientVital1-4', 'lblBedNumber' and 'lblPatientName' bits of code keep giving me the CS0122 error. Saying it is inaccessible due to its protection level. I have looked around the internet made the source files not read only but still have no luck.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HospitalMonitor
{
public class Controller
{
public CentralModule centralStationBedsideDetails;
public string module1Name, module2Name, module3Name, module4Name, bedName, patientName;
public CentralModule CentralStationBedsideDetails
{
get { return centralStationBedsideDetails; }
set { centralStationBedsideDetails = value; }
}
public void SetSelectedModules(string module1, string module2, string module3, string module4, string bednumber, string pName)
{
module1Name = module1;
module2Name = module2;
module3Name = module3;
module4Name = module4;
bedName = bednumber;
patientName = pName;
SetCentralStationBedsideDetails();
}
public void SetCentralStationBedsideDetails()
{
centralStationBedsideDetails.lblPatientVital1.Text = module1Name;
centralStationBedsideDetails.lblPatientVital2.Text = module2Name;
centralStationBedsideDetails.lblPatientVital3.Text = module3Name;
centralStationBedsideDetails.lblPatientVital4.Text = module4Name;
centralStationBedsideDetails.lblBedNumber.Text = bedName;
centralStationBedsideDetails.lblPatientName.Text = patientName;
}
}
}
If you look at the line
centralStationBedsideDetails.lblPatientVital1.Text = module1Name;
the error message is telling you that
lblPatientVital1
is a property or field of centralStationBedsideDetails that is declared as (mostly likely) private (and certainly not public). This is common in WinForms forms.
You can either modify that property to make it public (or internal, if this code is in the same assembly), or you can provide an additional wrapper property or method that sets/gets the value of that internal property/field and is visible to your code (public, or internal and in the same assembly).
You're not showing the code for it, but presumably the implementation for CentralModule has members (either properties or class-level variables) by those names. The error is telling you that you're trying to access them as though they are public, but they are not public.
They may be protected, internal, or private perhaps. But they are not public. And therefore your code can't directly access them on that object.
You can make them public, or you can make public members (properties or methods) which provide the functionality you're looking for. Perhaps something like this:
public Label PatientVital1
{
get { return lblPatientVital1; }
}
which you could use as:
centralStationBedsideDetails.PatientVital1.Text = module1Name;
Or, to de-couple consuming code from UI technologies, you can just expose a method:
public void SetPatientVital1Text(string text)
{
lblPatientVital1.Text = text;
}
which you could use as:
centralStationBedsideDetails.SetPatientVital1Text(module1Name);

How to implement the Singleton class for defining system variables?

I am trying to implement the Singleton Pattern.
Here is what I have done so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
namespace POS
{
public sealed class Settings
{
private static Settings instance = null;
private static readonly object padlock = new object();
//System Variables
public string SYSTEM_NAME { get; set; }
public Settings()
{
//start new connection to the database
dbConnetion db = new dbConnetion();
string sql = " SELECT system_name "
+ " FROM configuration "
+ " LIMIT 1";
//read the system variable
foreach (var i in db.getData(sql, null, r =>
new globalSettings()
{
_sys_name = r["system_name"].ToString()
}
)
)
{
SYSTEM_NAME = i._sys_name;
}
}
public static Settings Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Settings();
}
return instance;
}
}
}
}
class globalSettings
{
public string _sys_name;
}
}
And when I want to read the variable I do this:
GlobalName.Text = Settings.SYSTEM_NAME;
but this is not working.
I get the following error:
Error 1 An object reference is required for the non-static field, method, or property 'POS.Settings.SYSTEM_NAME.get'
How can I read the variables correctly? also did I manage to implement the Singleton pattern correctly?
In your code, you want:
GlobalName.Text = Settings.Instance.SYSTEM_NAME;
this will instantiate the singleton if it doesn't exist, and allow you to use its instance (stored in the Instance static property).
For storing such properties there is an easier pattern that can be used in C#:
public static class Settings
{
public static string SystemName { get; set; }
public const string SomeOtherProperty = "x";
public static int AnotherOne
{
get
{
return 42;
}
}
}
Be warned however that such global objects introduce a hidden dependency, making your code harder to maintain (changes in one place may affect distant, non-obvious places in code. - less apparent if all properties are constants though) and harder to test (you can't stub/mock a singleton).
To make your implementation a bit cleaner:
Your class violates the Single Responsibility Principle: you should separate reading the configuration from DB from the object that simply aggregates it.
Make the dependency on the configuration explicit. For example, each class that needs the configuration should accept its instance in the constructor and use it for its purposes. This way you can also easily test such class, providing different configurations and testing behaviour for different settings.
Then you can test your classes without using the database, but using some hardcoded settings. Imagine also, that you may want to read the settings from a file or a command line sometimes.

How to use the same info across multiple forms

I am working on my first C# program and have run into a brick wall. I want to be able to set and get variables throughout diferent forms in the same application.
I created a class called "data" which contains the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Application1
{
public class data
{
public string SearchAirport
{
get
{
return searchairport;
}
set
{
searchairport = value;
}
}
}
}
What do I need to put into my forms to be able to use this class??
Right now all I have is:
data.SearchAirport = commandAirport;
string working = data.SearchAirport;
I know I have to add something else to keep from getting the:
"Error 11 An object reference is required for the non-static field, method, or property 'Sector_Datastore_2._0.data.SearchAirport.get'..."
error
Well, you need to declare searchairport:
public class data
{
private string searchairport;
public string SearchAirport
{
get
{
return searchairport;
}
set
{
searchairport = value;
}
}
}
alternatively, you could let C# do that automatically by using the following code:
public class data
{
public string SearchAirport
{
get;
set;
}
}
You are accessing searchAirport statically, and the method itself is not static.
You can either add the static keyword to the SearchAirport method signature or create a data object and then call SearchAirport on that object.
I'd suggest a Service Locator pattern, but I'm afraid it's way too complicated for what the Question-poster wants to achieve.
Just in case it may be useful later on: Service Locator pattern
data d = new data();
....before those lines

Categories

Resources