Say I have this C# class
public class MyClass {
int a;
int[] b = new int[6];
}
Now say I discover this class using reflection and while looking at the fields I find that one of them is of type Array (ie: b)
foreach( FieldInfo fieldinfo in classType.GetFields() )
{
if( fieldInfo.FieldType.IsArray )
{
int arraySize = ?;
...
}
}
I know it's not guaranteed that the array has a field initializer that creates the array but if it does I would like to know the size of the array created by the field initializer.
Is there a way to call the field initializer ?
If there was I would do something like this:
Array initValue = call field initializer() as Array;
int arraySize = initValue.Length;
The only was I found is to create an instance of the whole class but I would rather not do it like this as it's overkill...
Well, you can't.
The following code:
public class Test
{
public int[] test = new int[5];
public Test()
{
Console.Read();
}
}
will be compiled as:
public class Program
{
public int[] test;
public Program()
{
// Fields initializers are inserted at the beginning
// of the class constructor
this.test = new int[5];
// Calling base constructor
base.ctor();
// Executing derived class constructor instructions
Console.Read();
}
}
So, until you create an instance of the type, there is no way to know about the array size.
I dont think you have an option but to create an instance of the class as it doesnt exist until you do that.
Related
I have just started learning OOP. I am making my own class which builds on an array. It has an attribute called length and an array that is of that length.
However, the actual value of length is only declared in the constructor, so my array is stuck as a private variable within the constructor.
How do I implement one such that the array has a certain user-chosen length and is able to be accessed by the class' methods?
public class myClass
{
private int length; //This is an attribute of my class
public myClass(int myLength)
{
length = myLength;
int[] myArray = new int[length];
}
}
I want myArray to be accessible but this is not possible because it is a local variable in the constructor. I think if it was in Python I could just make it a global variable. (Although I think that I would still like to keep this array private as it is also an attribute).
Thanks!
Note: This is not homework but rather something I've been challenging myself to do.
Here's how your class could look like, the OOP way:
public class MyClass
{
public readonly int Length;
public int[] Data { get; private set; }
public MyClass(int dataLength)
{
Length = dataLength;
Data = new int[dataLength];
}
}
Here:
The Data array is constructed with the user-specified length.
You can access both Length and Data from inside and outside the class
Once instantiated, the Length property cannot be modified
Another way would be to make Length return directly the Length property of the array, as long as it was instantiated:
public class MyClass
{
public int Length { get { return Data == null ? 0 : Data.Length; } }
public int[] Data { get; private set; }
public MyClass(int dataLength)
{
Data = new int[dataLength];
}
}
Revised answer as you have added more code to the question:
You have unwittingly solved your own problem. Take a look at your private int length; declaration. After the object the initialized with the constructor public myClass(int myLength), the length variable is still accessible within the object.
The sample code below has a new public int GetLengthPlusOne() method to access the length variable. Similarly, you can now use the myArray variable in another method.
MyOtherClass
public class MyOtherClass
{
public void SampleMethod()
{
MyClass cls = new MyClass(5);
Console.WriteLine(cls.GetLengthPlusOne()); //Output: 6
var arr = cls.myArray;
}
}
MyClass
public class MyClass
{
private int length; //This is an attribute of my class
/*
* Declaring this within the class instead of the constructor allows it
* to be persisted in an instance of the class. This is a property.
*/
public int[] myArray { get; set; }
public MyClass(int myLength)
{
length = myLength;
myArray = new int[length];
}
public int GetLengthPlusOne()
{
return length + 1;
}
}
Side note on naming conventions:
For C#, Class names start capitalized (MyClass), whilst instances of a class start with a lower-case (myClass). Have a look at the documentation for more info.
The following seems to be a class array?
Chemical.ChemicalName[IndexNumber]
It seems that there are several other fields associated with Chemical, such as Cost, Quantity, SupplierName (Chemical.Cost etc).
I was wondering what this type of variable is called? A class array? I've been searching online about arrays and can't seem to find any documentation on this.
And secondly, how do I declare such a variable?
Assuming it's a property, not an array , so you cannot access using an index,
public class Chemical
{
// Field
public string ChemicalName;
...etc
}
if chemical is an array , then you can declare like this,
Chemical[] Chemicals = new Chemical[200];
Then you can access the particular element using the index,
Chemicals[IndexNumber].ChemicalName
EDIT
If you want to have ChemicalName as a array inside the class,
public class Chemical{
public ChemicalName[] ChemicalNames = new ChemicalName[5];
]
you can access like this,
Chemical[] Chemicals = new Chemical[200];
c[index].ChemicalNames[index];
Variable would look something like that
public class Chemical{
public ChemicalName[] ChemicalNames = new ChemicalName[5];
...
}
So you can invoke it like that
Chemical c = new Chemical();
c.ChemicalNames[index];
OR, you can also declare the Array as static so you wont need an intance of the class to get the array e.g.
public class Chemical{
public static ChemicalName[] ChemicalNames = new ChemicalName[5];
...
}
to call a static, simply use class.variable/method name
Chemical.ChemicalNames[index];
It is a class property that implements an indexer. Usually this is an array, but it can be something else as long as it implements this[int index].
You can declare one by declaring it as a class property. For example,
class Book
{
public Book(int numPages)
{
Pages = new Page[numPages];
}
public Page[] Pages {get;}
}
You can then instantiate an instance and access a page.
Book myBook = new Book(100);
myBook.Pages[50]=new Page("Hi, welcome to Page 50");
Console.Write(myBook.Pages[50].GetText());
Let me consider this statement from the question Chemical.ChemicalName[IndexNumber], We can consider Chemical as a class or as an object of some other class. If it is a class means the ChemicalName will be a static.
Then comes the ChemicalName definitely it will be a collection(List/Array or something like that) or even an object of a class which having an indexer.
Case 1: consider Chemical is class and ChemicalName is a List of string So the Definition will be :
public class Chemical
{
public static List<string> ChemicalNames = new List<string>(){"name1","name 2"};
}
So that you can access a single name like the following:
string someChemicalName=Chemical.ChemicalNames[0]; // will be name1
Case 2: consider Chemical is an object of a class and ChemicalName is a List of string So the Definition will be :
public class Chemicals
{
public List<string> ChemicalNames;
}
Then you can access create the Chemical by using the following code:
Chemicals Chemical= new Chemicals();
Chemical.ChemicalNames=new List<string>(){"name1","name 2"};
Here also you can workout your statement like this
string someChemicalName=Chemical.ChemicalNames[0]; // will be name1
Let's parse Chemical.ChemicalName[IndexNumber]:
IndexNumber is probably some value of one of integer types - let guess int IndexNumber. Other options could be enum or any type as you can use indexer with any arguments.
[IndexNumber] is indexing something. Since there is no
Static Indexers? in C# it means ChemicalName can't be class name of static class like following
namespace Chemical {
static class ChemicalName{}
}
so it means that ChemicalName is either property or field of Chemical.
Now for Chemical there are more options
it could be static class with ChemicalName as static property:
static class Chemical{
public static string[] ChemicalName = new[] {"Food", "Poison"};
}
it could be local variable of some type that has ChemicalName as instance property:
class ChemicalType{
public string[] ChemicalName = new[] {"Food", "Poison"};
}
...
void MyMethod()
{
// implicitly typed, same as `ChemicalType Chemical`
var Chemical = new ChemicalType();
int IndexNumber = 1;
Console.WriteLine(Chemical.ChemicalName[IndexNumber]);
}
it could be field or property of your class (with any accessibility as to get Checmical.ChemicalName syntax to work for property it need to be used inside a method of your class)
class MyClass
{
// one of any combination:
// private field
ChemicalType Chemical = new ChemicalType();
// or protected automatic property
protected ChemicalType Chemical {get;set;}
// or public property
ChemicalType _chemical;
public ChemicalType Chemical {get {return _chemical;}}
...
}
Finally let's see what ChemicalName could be: the only requirement is to allow indexer by some type. including int. This gives very broad set of types as many of built in types support indexing.
array is most common one string[] ChemicalName
just string - somewhat strange given name of variable, but possible - string ChemicalName. When indexing will give single char result
List<string>
dictionary, this option allows broader range of indexing - i.e. by strings Dictionary<string,string> ChemicalName.
custom type implementing similar to public string this[int i] (or any other return type).
So I have a array of class Piece called board.
There are some sub classes of piece such as Bishop and Knight and Rook ect...
The array looks like this:
Piece[,] board = new Piece[8,8];
board[0,0] = new Bishop(constructor stuff);
board[0,1] = new Rook(constructor stuff);
ect...
Each time I initialize a new instance of Bishop/Knight/Rook I want it to have it's own array so I can do
the following:
board[0,0].array[0] = ect...
How would I do that?
Add the array to the Piece class. All the subclasses will get access to it.
class Piece
{
public int[] array = new int[100]; // or whatever
// rest of class definition
}
Your Piece class would need to have a field or property for the array defined within it.
public abstract class Piece
{
public Something[] array = new Something[ARRAY_SIZE];
...
}
Do consider though whether this is actually a good design.
Add a variable to the classes called array which is an array?
Ex.
public class Bishop : Piece
{
// ...
public T[] array;
public Bishop()
: base()
{
// Initialize array
}
}
Where T is the type of the array.
You could make the class generic in case the array type might differ.
public class Bishop<T> : Piece
Then initialize it like:
board[0,0] = new Bishop<int>(); // array is int[]
I'd suggest you actually learning the basics of the language.
I have library and console program, which get this is library dynamically. In library on class exist int array. So. Can I on program, use reflector get this array? This is code of library:
public class Class1
{
public int [] arrayInt;
public Class1()
{
arrayInt = new int[5] {1,2,3,4,5};
}
}
This is code of program:
Assembly asm = Assembly.LoadFile(#"C:\TestLibrary.dll");
Type Class1 = asm.GetType("TestLibrary.Class1") as Type;
var testClass = Activator.CreateInstance(Class1);
PropertyInfo List = Class1.GetProperty("arrayInt");
int[] arrayTest = (int[])List.GetValue(testClass, null);//throw exception here
Console.WriteLine("Length of array: "+arrayTest.Count);
Console.WriteLine("First element: "+arrayTest[0]);
You get the exception because public int[] arrayInt; is not a property but a member variable, thus Class1.GetProperty(...) returns null.
Alternative 1) Use GetMember instead of GetProperty
MemberInfo List = Class1.GetMember("arrayInt");
Alternative 2) Declare a property in Class1
public int[] ArrayInt
{
get { return arrayInt; }
}
And change ethe reflection code to:
PropertyInfo List = Class1.GetProperty("ArrayInt");
Also, please note that your code shouldn't even compile, as an array does not have a Count property, but only a Length property. The following line should give a compilation error:
Console.WriteLine("Length of array: "+arrayTest.Count);
and should read
Console.WriteLine("Length of array: "+arrayTest.Length);
Use
Class1.GetMember("arrayInt");
instade Of
Class1.GetProperty("arrayInt");
You are creating a field in your original class but reflecting it as a property!
public class Class1
{
public int [] arrayInt {get;set;} // <-- now this is a property
public Class1()
{
arrayInt = new int[5] {1,2,3,4,5};
}
}
Only added () after arrayTest.Count:
Assembly asm = Assembly.LoadFile(#"C:\TestLibrary.dll");
Type Class1 = asm.GetType("TestLibrary.Class1") as Type;
var testClass = Activator.CreateInstance(Class1);
PropertyInfo List = Class1.GetProperty("arrayInt"); // <!-- here you are looking for a property!
int[] arrayTest = (int[])List.GetValue(testClass, null);//throw exception here
Console.WriteLine("Length of array: "+arrayTest.Count());
Console.WriteLine("First element: "+arrayTest[0]);
I need advice on structures.
I have 2 sections of code. The first section is as below:
namespace Project.GlobalVariables
{
class IOCard
{
struct InputCard
{
public string CardNo;
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo = new int[16];
public int[] ReadBitNo = new int[16];
}
static InputCard[] InputCards = new InputCard[5];
public static string ACardNo = InputCards[1].CardNo;
public static string BCardNo = InputCards[2].CardNo;
}
}
The second portion is as below:
private void Form1_Load(object sender, EventArgs e)
{
IOCard.ACardNo = "Card A";
IOCard.BCardNo = "Card B";
MessageBox.Show(IOCard.ACardNo);
MessageBox.Show(IOCard.BCardNo);
}
My plan is to be able to assign and retrieve InputCards component by using IOCard as shown in Form1_Load.
However, when I compile the code, I get the following error.
Error 1 'Project.GlobalVariables.IOCard.InputCard.WriteBitNo': cannot have instance field initializers in structs E:\Programming\New platform\StandardPlatform\StandardPlatform\Project\GlobalVariables.cs 16 26 StandardPlatform
Can someone tell me how to solve the error?
Please advise. Thanks.
Here are the classes that I have attempted to create and use, but failed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Project.GlobalVariables
{
static class IOCard
{
public const int TotalInputCard = 10;
public const int TotalOutputCard = 10;
public class InputCard
{
public string CardNo = "1";
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo = new int[16];
public int[] ReadBitNo = new int[16];
}
public class OutputCard
{
public string CardNo;
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo = new int[16];
public int[] ReadBitNo = new int[16];
}
public static InputCard[] InputCards = new InputCard[TotalInputCard];
public static OutputCard[] OutputCards = new OutputCard[TotalOutputCard];
public static int X100 = InputCards[0].WriteBitNo[0];
public static int Y100 = OutputCards[0].WriteBitNo[0];
}
}
I tried to use these in the Form_Load, like so:
private void Form1_Load(object sender, EventArgs e)
{
IOCard.X100 = 1;
IOCard.Y100 = 1;
}
No matter how much I have tried to search on the net for answers, I have got nowhere.
Please advise. Thanks.
In C#, a struct value is not a reference to an object in the way a value of a class type is. The value of a struct is the "union" of all the values of the instance fields of the struct.
Now, the default value of a struct type is the value where all those fields have their default values. Since the beginning of C#, the syntax:
new S() // S is a value-type
where S is a struct type, has been equivalent to the default value of that struct. There is no constructor call! This is the exact same value which can (nowadays) also be written
default(S) // S is a value-type
Now, things like
struct S
{
int field = 42; // non-static field with initializer, disallowed!
// ...
}
are illegal (cannot have instance field initializers in structs). They could give the impression that the field of a new S() would be 42, but in fact the field of new S() must be the default value of int (which is zero, distinct from 42).
With this explanation, you also see why it is not possible to create a non-static, zero-parameter constructor for a struct type, in C#.
What's it's trying to say is that when you have InputCards = new InputCard[5]; it will allocate a block of memory 5 times the size of an InputCard structure and set all of its bytes to 0. There is no opportunity to execute the int[] WriteBitNo = new int[16]; and such assignments, so you cannot have them.
Your options are to either manually call an initializer for your structs or make it a class and manually initialize the InputCards array with 5 new instances of InputCard.
You will neither be able to initialize a struct's fields nor define a default constructor to initialize it's fields. After looking at your struct, I recommend you use a class instead. It's not recommended to use a struct for a scenario where you have a bunch of fields.
Try this. Initialize the InputCard with a factory function Create():
namespace Project.GlobalVariables
{
class IOCard
{
struct InputCard
{
public string CardNo;
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo;
public int[] ReadBitNo;
static InputCard Create()
{
return new InputCard()
{
CardNo = string.Empty,
WriteBitNo = new int[16],
ReadBitNo = new int[16]
};
}
}
static InputCard[] InputCards = new InputCard[]
{
InputCard.Create(),
InputCard.Create(),
InputCard.Create(),
InputCard.Create(),
InputCard.Create()
};
public static string ACardNo = InputCards[1].CardNo;
public static string BCardNo = InputCards[2].CardNo;
}
}
Use class instead of structure. Structure is used for small types like Point, which are faster to create on the stack and copy, than to create dynamically and pass by reference.
Not sure about the exception, but i have a solution.
You should not use "struct" for this class, it is too much (and storing too much data). If you define it as "class", the same code would work fine.
Is there a particular reason why you want this to be a struct rather than a class?
If you make it a class, it works just fine.