Trouble using a child class - c#

I am attenmpting to initialise an instance of a class from a base class, where the class to be used will be determined in a switch case, as follows:
ImportBase.cs: (Parent abstract class and interface)
namespace WebApi.Services.Import.Investments
{
interface IImport
{
public void Import(IFormFile file, int UserId);
}
public abstract class ImportBase : IImport
{
public abstract void Import(IFormFile file, int UserId);
}
}
ImportKuflink.cs (Child class)
namespace WebApi.Services.Import.Investments
{
public class ImportKuflink : ImportBase
{
public override void Import(IFormFile file, int UserId)
{
System.Diagnostics.Debug.WriteLine("Import Kuflink");
}
}
}
ImportFleet.cs (Child class)
namespace WebApi.Services.Import.Investments
{
public class ImportFleet : ImportBase
{
public override void Import(IFormFile file, int UserId)
{
System.Diagnostics.Debug.WriteLine("Import Fleet");
}
}
}
and in my controller:
namespace WebApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class InvestmentsController : ControllerBase
{
public void Import()
{
ImportBase importService = null;
switch(investmentEntity)
{
case "kuflink":
ImportKuflink importService = new();
break;
case "Fleet":
ImportFleet importService = new();
break;
}
if (importService != null)
{
importService.Import(file, UserId);
}
}
}
}
When I attempt to use the instance, I get the error "'importService' does not exist in the current context"
Is there a way I can test if importService was initialised and use it outside of the case statements?
I am basically have a base class and then a few other classes which will inherit from this class, and depending on the type of import occuring the appropriate child class will be used. How can I go about this?

Your error message »'importService' does not exist in the current context« does not match your program and I doubt that the code as shown will show this error.
I assume that you are actually seeing a different error. Let's first reduce your code to the smallest possible program to exhibit the (a?) problem.
public class Program
{
public static void Main()
{
Console.WriteLine("Enter name:");
var input = Console.ReadLine();
Object importService = null;
switch(input)
{
case "x":
String importService = "";
break;
}
if (importService != null)
{
Console.WriteLine(importService);
}
}
}
The scope/lifetime of a variable in C# is generally determined by its enclosing block. Declaring a variable (with TypeName variableName) makes this variable visible inside its block. When execution exits the block, the variable is no longer accessible.
Assigning a variable is done with the assignment operator (i.e. =), but without specifying the type name, e.g. variableName = value.
Blocks are opened with { and closed with }. Blocks can be (arbitrarily) nested. Variables in inner blocks shadow variables of the outer blocks. Variables of outer blocks are accessible in inner blocks, unless shadowed.
When you declare a variable in an inner block with a name identical to an existing variable in an outer block, the variable of the outer block becomes inaccessible to the current block and any of its descendants.
In the example, you have 4 relevant blocks:
The class
The method
The switch statement
The if statement
The method declares a variable importService and immediately assigns it the value null: Object importService = null. The declaration and assignment could be split, to make it more explicit:
Object importService; // declaration
importService = null; // assignment
The switch statement opens a new block, and this block declares a new and independent variable, which shadows the variable of the outer block (the method): String importService. Once the outer variable is shadowed, it cannot be accessed from within the inner block. Assigning the inner variable has no effect on the shadowed variable.
In other words: the switch statement declares and assigns a new variable of type String and name importService, then does nothing with it. Once the switch statement is exited, the variable is gone and no longer accessible.
After leaving the switch statement's block, the shadowed variable (importService of type Object) becomes accessible again. But nothing happened with this variable, its value is still null – remember: variables with identical name in nested blocks are distinct and independent.
What do you need to do to "fix" your program? Don't declare a new variable with the same name, but instead assign the original variable. This was already mentioned in the comments under the question:
ImportKuflink importService = new(); declares and assigns a new block-local variable. It does not assign your existing field this.importService. Both exist independently. […]
public class Program
{
public static void Main()
{
Console.WriteLine("Enter name:");
var input = Console.ReadLine();
Object importService = null;
switch(input)
{
case "x":
importService = ""; // assign, don't redeclare!
break;
}
if (importService != null)
{
Console.WriteLine(importService);
}
}
}

Related

How do I cache FieldInfo.GetValue()?

Okay so I made a class to take an input script and a property name. It finds the property and then displays it when Display() is called [The display method is overridden by a child class]. However, there is one problem that I have with it and that is how do I cache what FieldInfo.GetValue() returns? It would be preferable to have a pointer or something to reuse once the variable containing what I need is found since reflection is costly.
public class PropertyDisplayer : MonoBehaviour
{
public string PropertyName;
public Object TargetObject;
public object _TargetObject;
public FieldInfo Property;
void Start()
{
_TargetObject = TargetObject;
if (!PropertyName.Contains("."))
{
Property = _TargetObject.GetType().GetField(PropertyName);
}
else
{
string[] split = PropertyName.Split('.');
Property = _TargetObject.GetType().GetField(split[0]);
for (int i = 1; i != split.Length; i++)
{
_TargetObject = Property.GetValue(_TargetObject);
Property = Property.FieldType.GetField(split[i]);
}
}
}
public virtual void Display()
{
}
}
Use the dynamitey library, it will cache all reflection calls transparently for you and make them near normal call speed. More info here: https://github.com/ekonbenefits/dynamitey
You can store the results of the FieldInfo.GetValue() call in a new member field that is of type "object". (This IS, effectively, a pointer)
Then, in your Display() override, simply cast this member to the appropriate class, in order to access its members.

Is possible to declare multiple times same name variable in c# within Ne-stead/same Scope?

namespace project1
{
public class testclass
{
int i = 0;
public int foobar()
{
int i = 1;
return i;
}
}
}
Result:
1
I am declaring two times of i variable. Why does c# compiler allowing me this ?
If I will try to declare in same scope then compiler will give me exception so why allowing in nested scope?
This is not a bug
Your field int i in the class declaration can be accessed at any time with this.i so there is no overlap. In fact, this is actually the convention to write (private) field names, parameters and local variables within methods in camelCase. Properties, method names, class names etc. on the other hand are written in PascalCase.
So in your class if you want to access the field i of your class, you can do so by writing this.i. Otherwise you will access the scope designated variable i as long as you are within the if-block.
namespace Project1 // PascalCase here for namespace name
{
public class TestClass // Again PascalCase for class name.
{
int i = 0; // camelCase correct here for field name.
public void Foobar() // PascalCase for method name.
{
if (0 == 0)
{
int i = 0; // camelCase correct here for local variable name.
// Cannot be re-declared until your if-block is finished.
// accessing both elements named 'i'
this.i = i;
}
return;
}
}
}
See Microsoft docs for further reference.
All variables declared in a particular scope have to be unique.
You can reuse some variables depending on their data types, but whether or not you should reuse a variable depends on what you're doing.
Your code can work, but you're declaring i a second time which is incorrect as it already exists with a value of 0.
You need to change its value instead of trying to recreate the variable:
namespace project1
{
public class testclass
{
int i = 0;
public void foobar()
{
if (0 == 0)
{
i = 0;
}
return;
}
}
}
You could also create a new variable:
namespace project1
{
public class testclass
{
int i = 0;
public void foobar()
{
if (0 == 0)
{
int j = 0;
}
return;
}
}
}

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();
}
}

Classes & Constructors: Incrementing a variable outside class

I am currently working with classes and constructors. I have sint variable named current that equals 0 inside the constructor. Now when I click the button I am trying to increment property current and then call GetNextTree to display. But when incrementing current++ from button click I receive this error: does not exist in current context. What would be the proper way to increment current then?
public class fruit_trees
{
}
public class ListForTrees
{
public int current;
public fruit_trees GetNextTree()
{
current = 0;
fruit_trees ft = first_tree;
int i = 0;
while (i != current)
{
ft = ft.next_tree;
i++;
}
return ft;
}
}
private void ShowNextItem_Click(object sender, EventArgs e)
{
//Show Last Item
fruit_trees obj = mainlist.GetNextTree();
if (obj == null)
{
labelSpecificTree.Text = "No more trees!";
}
else
{
//error: current does not exist?
current++
labelSpecificTree.Text = obj.next_tree.GetTreeType.ToString();
}
}
The problem is in the scope (encapsulation) where you try to call int current.
From your posted code, int current is defined in class ListForTrees. However you are trying to access it without initializing your object of type ListForTrees.
In addition, you are using mainlist which is also not defined in your posted code. Please, post complete code coverage of items that you use in code.
Your current variable is encapsulated inside your ListForTrees class. Since this variable is an instance variable, you need to create a new instance of ListForTrees in order to access this variable using the instance.variable syntax.
Moreover I believe your class design is quite flawed. I think what you should redesign your class like:
public class FruitTree
{
public static int Current { get; set; }
public FruitTree GetNextTree()
{
//your code here
}
}
And then you can initialize a list of trees in your code

accessing objects from a class

I have created a seperate .cs file names aNameClass.cs and have stored the following class in it.
I am able to iniatiate it in my Main() statment, but when I try to access the GetChoice object, it tells me it is inaccesable due to invalid prividlidges.
here is my code to iniatiate it and access it.
namespace aNameCollector
{
// ...
csGetChoice gc = new csGetChoice();
choice = gc.GetChoice(); //invalid prividlidges???
class csGetChoice
{
static string GetChoice()
{
string choice = " ";
Console.WriteLine("++++++++++++++++++=A Name Collector+++++++++++++++");
Console.WriteLine();
Console.WriteLine("What would you like to do?");
Console.WriteLine("E = Enter a Name || D = Delete a Name || C = Clear Collector || V = View Collector || Q = Quit");
choice = Console.ReadLine();
return choice;
}
}
You need to use a static reference and specify public for the method like this:
// static access:
choice = csGetChoice.GetChoice();
...
public static string GetChoice() { ...
or make the method an instance method instead of static and define and access it like this:
// instance access:
csGetChoice gc = new csGetChoice();
choice = gc.GetChoice();
...
public string GetChoice() { ... // static keyword removed
If you don't provide an access modifier the default is private and therefore visible only to the class that contains it and not to any other classes.
Make the method public and call the static method on type not on the instance
csGetChoice.GetChoice();
The static key word for a type member states that you can access it by referencing the class directly and not its objects. However, you still need the right access modifier to be able to access that member.
private is the default value when you don't explicitly declare the access modifier in C# as in your case. And that allows you to access that member only inside its class.
To be able to access it from outside the class you need to explicitly use other access modifiers as public.

Categories

Resources