Access object property in array - c#

class Program{
static void Main(string[] args){
object[] array = new object[1];
CreateItem item = new CreateItem();
item.name = "Necklace";
item.value = 5;
array[0] = item;
Console.WriteLine(array[0].name); //This part of the code doesn't work.
//It can't find the property name.
Console.ReadLine();
}
}
public class CreateItem {
public string name;
public int value;
}
Hi there! First of all I'd like to say that I'm not very familiar with objects, so excuse any mistakes you can see in the code (Although feel free to correct them, it'd be a great way to learn).
I've been working on making a small game using C#, but I came across a problem: I can't access my object properties when I put them in an array. Does anyone know which code I should use to be able to access my object properties while they're in an array?
Thanks for reading, and once again, excuse any silly mistakes I made, I'm fairly new to working with objects.

You shouldn't use an object array when you have a strong type that you're interested in using (and you know the type already).
CreateItem[] array = new CreateItem[1];
CreateItem item = new CreateItem();
item.name = "Necklace";
item.value = 5;
array[0] = item;
Console.WriteLine(array[0].name);
Necklace will now be outputted as expected.

You should probably look at using Generics and Lists, it is a very common and a valuable concept to grasp, as is the concept of Boxing and Unboxing which Generics solves.
class Program{
static void Main(string[] args){
List<CreateItem> list = new List<CreateItem>();
CreateItem item = new CreateItem();
item.name = "Necklace";
item.value = 5;
list.Add( item );
Console.WriteLine(list[0].name); //This part of the code doesn't work.
//It can't find the property name.
Console.ReadLine();
}
}

You could cast the object to your type, i.e.:
Console.WriteLine(((CreateItem)array[0]).name);
or (more effectively)
define your array as CreateItem[] array = new CreateItem[1];

Line
object[] array = new object[1];
creates an array of elements of type Object which is the base class for all other classes in .NET.
When you do:
array[n] = item;
an implicit conversion to the base type occurs and through array[n] you can access only members of the Object type portion of the CreateItem object (like ToString() or GetType() - their overrides will be called).
If you want to access entire CreateItem object, you have to cast the reference to the base type back to the original type, by using cast operator for example:
var name = ((CreateItem)array[0]).name;
This explicit casting is error-prone, has a run-time overhead and it is a sign of the poor design. When you know the type of the collection in advance, declare the collection of that type as other answers are suggesting:
// you can use array if you know number of items in advance and that number of elements will not change
CreateItem[] array = new CreateItem[N];
// use list if number of elements might change
List<CreateItem> list = new List<CreateItem>();

Related

Does C# Compiler Treat Array Element Differently From The Same Type Variable?! (System.ArrayTypeMismatchException)

I was working on something today, but I noticed something in the code, so let me explain the situation.
I get data as IEnumerable from different concreate classes of interface, look at this sample code
public interface ISampleClass {
IEnumerable<ISampleClass> GetSampleData();
}
public class SampleClassAsPerson : ISampleClass
{
public IEnumerable<ISampleClass> GetSampleData()
{
// Return data from DB
// Return list for this example
return new List<SampleClassAsPerson>() {new SampleClassAsPerson(), new SampleClassAsPerson()};
}
}
I put the data that comes from GetSampleData() method in Array of my Interface(will see this in the code bellow), I noticed that my Array not holding the same data as the variable from the same type, lets look at the code
public class TestArrayAndSingle
{
public const byte ARRAY_SIZE = 10;
public const byte FIRST_PLACE_IN_ARRAY = 0;
public static void DoTheTest()
{
// First as single element
IEnumerable<ISampleClass> singleIEnumerable = new List<ISampleClass>();
singleIEnumerable = new SampleClassAsPerson().GetSampleData(); // Works Fine for variable of type new List<ISampleClass>()
// Second as array of elements
IEnumerable<ISampleClass>[] arrayOfIEnumerable = new IEnumerable<ISampleClass>[ARRAY_SIZE];
arrayOfIEnumerable[FIRST_PLACE_IN_ARRAY] = new SampleClassAsPerson().GetSampleData(); // Works fine also
IEnumerable<ISampleClass>[] arrayOfList = new List<ISampleClass>[ARRAY_SIZE];
arrayOfList[FIRST_PLACE_IN_ARRAY] = new SampleClassAsPerson().GetSampleData(); // Return ArrayTypeMismatchException, This should be the same as singleIEnumerable I guess(new List<ISampleClass>())
}
}
So for the first one IEnumerable singleIEnumerable = new List(); works fine but in IEnumerable[] arrayOfList = new List[ARRAY_SIZE]; first item gives exception (System.ArrayTypeMismatchException - Attempted to access an element as a type incompatible with the array.).
Can anyone explain why? does element in array treated differently from same type variable?!
You have a collection of List<T> and are trying to store a more generic IEnumerable<T> in it. but not all IEnumerable<T>s are lists, so the compiler doesn't allow it. What if the IEnumerable<T> was a stack? or a linked list? or a LINQ query?
With a array of a certain type, you can store items that are of that type or more specific, but not less specific. So you can store lists in an array of enumerables (because all lists are enumerables), but not enumerables in an array of lists (since not all enumerables are lists).

Changing one property in multidimensional array changes other array's properties too

I have a multidimensional array called SensorGetResult. In some cases this array can have a single array in it. If that happens, I must copy this one array and add it again, so I must have 2 arrays now. Then I need to change these array's dateTime property. This is my code:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
sensorGet.SensorGetResult[0].dateTime = end; //It works correctly including this line
sensorGet.SensorGetResult[1].dateTime = start; //At this line both array's dateTime property changes
Why can't I assign dateTime properties to each array individually?
It looks like you are using a reference type for your helperArray.
When the following code executes:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
What actually happens is you take a the first element of SensorGetResult which is a reference to the object (which I believe you intend to copy) and append the reference to the list thus resulting in a list which has two references to the same object in the memory.
If you want it to make a copy of the object, you have to implement that by yourself. Usually this means creating a new object of the same type and copying all the properties.
var objectToCopy = sensorGet.SensorGetResult[0];
var helperArray = new WhatEverTypeIsYourHelperArray {
Property1 = objectToCopy.Property1,
Property2 = objectToCopy.Property2,
// etc.
};
sensorGet.SensorGetResult.Add(helperArray);
But you have to be aware if any of the properties is furthermore a reference type, you need to do this recursively for all the properties.
If WhatEverTypeIsYourHelperArray is type you own, you could utilize Object.MemberwiseClone method and make it all easier for yourself. You can do this by implementing a method like the following. As a note, MemberwiseClone is a protected method hence the need of a new method in your class.
public WhatEverTypeIsYourHelperArray Clone() {
return (WhatEverTypeIsYourHelperArray)this.MemberWiseClone();
}
But even the MemberwiseClone() method doesn't copy reference types for you, rather just copies the pointers to the objects which means that all the properties of reference type of both the original and the cloned object will point to the same objects in the memory.
SensorGetResult row seems to be a reference type.
So when you wrote
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
you actually said that new row in SensorGetResult will point to the same object as the first one.
You can implement method like below:
public SensorGetResultRow Clone()
{
return new SensorGetResultRow (this.field1, this.field2, etc...)
//or if you use parameterless constructor
return new SensorGetResultRow ()
{
field1 = this.field1
//etc.
}
}
and use it:
var helperArray = sensorGet.SensorGetResult[0].Clone();

Enumerable.Repeat for reference type objects initialization

I have a question about Enumerable.Repeat function.
If I will have a class:
class A
{
//code
}
And I will create an array, of that type objects:
A [] arr = new A[50];
And next, I will want to initialize those objects, calling Enumerable.Repeat:
arr = Enumerable.Repeat(new A(), 50);
Will those objects have the same address in memory?
If I will want to check their hash code, for example in that way:
bool theSameHashCode = questions[0].GetHashCode() == questions[1].GetHashCode();
This will return me true, and if I will change one object properties, all other objects will change it too.
So my question is: is that properly way, to initialize reference type objects? If not, then what is a better way?
Using Enumerable.Repeat this way will initialize only one object and return that object every time when you iterate over the result.
Will those objects have the same address in memory?
There is only one object.
To achieve what you want, you can do this:
Enumerable.Range(1, 50).Select(i => new A()).ToArray();
This will return an array of 50 distinct objects of type A.
By the way, the fact that GetHashCode() returns the same value does not imply that the objects are referentially equal (or simply equal, for that matter). Two non-equal objects can have the same hash code.
Just to help clarify for Camilo, here's some test code that shows the issue at hand:
void Main()
{
var foos = Enumerable.Repeat(new Foo(), 2).ToArray();
foos[0].Name = "Jack";
foos[1].Name = "Jill";
Console.WriteLine(foos[0].Name);
}
public class Foo
{
public string Name;
}
This prints "Jill". Thus it shows that Enumerable.Repeat is only creating one instance of the Foo class.
When using the following code to create an array:
var foos = Enumerable.Repeat(new Foo(), 2).ToArray();
The reason why each location in the array is the same is because you are passing an object, and not a function that creates an object, the code above is the same as:
var foo = new Foo();
var foos = Enumerable.Repeat(foo , 2).ToArray();
The reason above also explains why using a Select statement, like in the code below, creates a new object for each entry, because you are passing a function that dictates how each object is created, rather than the object itself.
Enumerable.Range(1, 2).Select(i => new Foo()).ToArray();
I would use a simple for loop to populate an array with new reference types.

C# Get members of Generic object

I will highly appreciate any help. I have a string as an input. Let's call it as 'table_name'. I want to create a collection of table_names (where table_name is an actual object in a referenced project). I did the following:
Object obj = Activator.CreateInstance("ClassLibrary", "ClassLibrary." + table_name);
Type CollectionType = typeof(Collection<>).MakeGenericType(new[] { obj.GetType() });
ICollection c = (ICollection)Activator.CreateInstance(CollectionType);
Then I called a method (which is located in different project and which returns Collection of objects) to fill out my ICollection object (in this case, c):
object[] parameters = new object[] { x_coord, y_coord, buffer_dist};
c = (ICollection)sde_db.GetType().GetMethod("Method" + table_name).Invoke(sde_db, parameters);
So far everything works fine. I can see the number of elements in the collection. But the problem is when I try iterate through the collection, it doesn't show its elements. I can see them only during run time. Is there a way to retrieve members of the collection during compile time? I want something like:
for(int i = 0; i < c.Count; i++){
label.Text = c[i].Details;
}
Thanks!
Yes, but you have to answer the question of what members are there and how you plan to use them (and most importantly, how the compiler knows what you mean when you write the code referring to them, e.g. .Details). E.g. if your objects will always implement an interface...
public interface IHasDetails
{
string Details { get; }
}
Then you can cast the object to that type:
label.Text = ((IHasDetails)c[i]).Details;
If you just want to access it dynamically, use dynamic:
label.Text = ((dynamic)c[i]).Details;
ICollection does not define an index accessor so you cannot do c[i].
Can't you use an IList instead?
In order to access the members during compile time you need to be using a concrete type that has the members available. Given that your dynamically accessing the types involved this doesn't seem possible unless they have a common, and known, base type or interface. Without one of those mechanisms you are reduced to some kind of dynamic access.
Since I needed only details about each element in the ICollection, I used System.Xml.Serialization to get a string containing info on a particular element in the collection.
for (int i = 0; i < c.Count; i++)
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(c[i].GetType());
using (StringWriter writer = new StringWriter())
{
x.Serialize(writer, c[i]);
String details = writer.ToString();
}
//do here what ever you want
}

C# - Why do you need to instantiate each array element?

I use this type of construct often.
response = new LocationResponse ();
response.LocationDetails = new LocationDetail[4];
response.LocationDetails[0] = new LocationDetail();
response.LocationDetails[0].site = "ABCDE";
...
The piece I don't fully understand is this piece:
response.LocationDetails[0] = new LocationDetail();
Why does each individual element of the array have to be instantiated?
If you leave it out, you get undefined exceptions.
Well if an array elements were automatically instantiated, you would be locked into which classes you defined the array with. You wouldn't be able to use arrays of abstract classes or interfaces or any object which doesn't have a default constructor. This is just naming a few problems with automatically instantiating objects in an array.
You don't need to instantiate LocationDetail if it is a value type (struct). You have to instantiate it if it's a class because the default value for a reference type is null.
This code works:
public struct LocationDetail
{
private string site;
public string Site
{
get { return site; }
set { site = value; }
}
}
static void Main(string[] args)
{
LocationResponse response = new LocationResponse();
response.LocationDetails = new LocationDetail[4];
response.LocationDetails[0].Site = "ABCDE";
Console.Write(response.LocationDetails[0].Site);
}
Think of it as a reference pointer, if you don't initialize you have a null value on each of the LocationDetail items.
It set each element to null, which is the default value for a class until you say otherwise.
How can C# automatically instantiate the elements for you? Perhaps that type in your array doesn't even have a default constructor. Maybe you don't want to consume the memory required by an entire array of elements before you actually have them.
I don't see the advantage to having the runtime try to fill that array in advance.
I think of arrays as egg-cartons. You can declare an egg carton, but that doesn't put eggs in your carton - you still have to decide what kind of egg to put in each slot of the carton. You might want easter eggs in some, but grade AA free-roam brown eggs in others.
The array itself can't know what you want ahead of time, so it defaults to putting nothing in your slots.
It would be a costly operation, depending on what needs to be done to create the objects. Furthermore, what if the objects cannot simply be instantiated? Or if you need an array of a given type, where only some fields will eventually contain an object and others shoud be null?
As others have pointed out, the array will be full of nulls after you declare it. You can simplify your initialization logic somewhat by doing this:
response.LocationDetails = new LocationDetails [] {
new LocationDetails(),
new LocationDetails(),
new LocationDetails(),
new LocationDetails()
};
And what would be even nicer is if the LocationDetails constructor had an overload that took a siteid:
response.LocationDetails = new LocationDetails [] {
new LocationDetails("ABCDE"),
new LocationDetails("FGHIJ"),
new LocationDetails("KLMNO"),
new LocationDetails("PQRST")
};
Or the super fancy C# 3.0 stuff that Justice points out :)
response = new LocationResponse() {
LocationDetails = new LocationDetail[] {
new LocationDetail { site = "ABCDE" }
}
}

Categories