First, I'm going to apologize if this is a stupid question. I've been using C# for 16 hours after having not programmed anything since VB6. I'm just trying to hack together a small program for personal use that reads from an old access database and spits out a formatted report in Excel. I apologize for the messy/inefficient code.
Overview: I have two class types, "Zone" and "Device". Each "Zone" has a List of Devices in it. The main program has a List of Zones. Each database has a varying number of "zones" in it, and each "zone" has a varying number of devices assigned to it. I need to parse, sequentially, the zone list and the devices on each zone. I started with structs and arrays and popular opinion seems to be that those are both bad ways to do it, and I wasn't having much luck anyway, so I moved to lists and classes, and it was going well.
I can pull all the "zones" from the database, add them to the list, assign them their labels and IDs. The problem is when I go to read the "devices" from the database, I can't add them to the list within the Zone.
This is the error I get: "Object reference not set to an instance of an object." Which I gather means the object is null?
Here's the relevant code:
Device Class:
public class Device
{
public string Label;
public string Address;
public string Type;
public Device(string Label, string Address, string Type)
{
this.Address = Address;
this.Label = Label;
this.Type = Type;
}
}
Zone Class:
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
// ADDED AS PER SUGGESTIONS BELOW
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
Initializing and populating Zone List (on button click) (completes successfully)
List<Classes.Zone> Zones = new List<Classes.Zone>();
dbZoneReader = myZoneSelect.ExecuteReader();
while (dbZoneReader.Read())
{
Classes.dbItem dbRow = new Classes.dbItem();
dbRow.Address = Convert.ToInt16(dbZoneReader["DeviceAddress"].ToString());
dbRow.DeviceType = Convert.ToInt16(dbZoneReader["DeviceType"].ToString());
dbRow.Label = dbZoneReader["DeviceLabel"].ToString();
if (dbRow.Label != "" && dbRow.Address > 0)
{
Zones.Add(new Classes.Zone(dbRow.Label,dbRow.Address));
}
}
Adding Devices to their respective Zones:
while (dbReader.Read()) {
Classes.dbItem dbRow = new Classes.dbItem();
string tempZones;
// Acquire/convert device information
dbRow.Node = Convert.ToInt16(dbReader["NodeAddress"].ToString());
dbRow.Loop = Convert.ToInt16(dbReader["LoopSelection"].ToString());
dbRow.Address = Convert.ToInt16(dbReader["DeviceAddress"].ToString());
dbRow.TypeID = Convert.ToInt16(dbReader["TypeID"].ToString());
dbRow.FlashScanID = Convert.ToInt16(dbReader["FlashScanID"].ToString());
dbRow.DeviceType = Convert.ToInt16(dbReader["DeviceType"].ToString());
dbRow.Label = dbReader["DeviceLabel"].ToString();
// Find "proper" zone ID (some zones have multiple IDs, only one is relevant)
tempZones = dbReader["DevicePointMappingList"].ToString();
tempZones = tempZones.Replace("Z", "");
var elements = tempZones.Split(new[] { ',' }, System.StringSplitOptions.RemoveEmptyEntries);
if (elements.Length >= 2) {
ZoneCheck z = new ZoneCheck();
foreach (string items in elements) { if (z.Check(items)) { dbRow.Zone = Convert.ToInt16(items); } }
} else {
if (elements.Length == 1) { dbRow.Zone = Convert.ToInt16(elements[0]); }
else { dbRow.Zone = 0; }
}
// Only add devices that aren't assigned to zone 0, which is non-existent
if (dbRow.Zone > 0) {
// Add new device to zone's device list [THIS IS WHERE IT FAILS]
Zones.Find(z => z.ID == dbRow.Zone).Devices.Add(new Classes.Device("Test", "test", "Test"));
}
}
I've gone through and found out exactly where it fails, and it's the last line where it tries to add the device. Searching here and on google has lead me to believe that I need to initialize the object list... which I believe I've done? I've tried initializing it within the Zone class constructor, and when the Zone is added (which is what it's set too now).
I've confirmed that the Zone object exists, and that the Detectors list within that Zone object isn't null. Kinda stumped, figure I'm doing something that I shouldn't be doing and just don't know better, or I'm missing something really obvious.
The problem is in your Zone
class. You need to initialize the List<Device>
as follows.
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
The reason is that when you write public List<Device> Devices;
, you're not actually creating an object. You're creating a variable that can hold an instance of the specified object. It's only when you pair the variable declaration up with object initialization ( = new List<Device>();
) that you get a usable instance of the object.
Thinking of the same issue in terms of a simpler object may help:
public class Foo
{
public string bar; // bar isn't an actual instance of an object, it's just a spot that can hold a string
public void ManipulateBarWithRuntimeError()
{
bar.Substring(0, 1); // "bar" isn't actually set to anything, so how can we take a substring of it? This is going to fail at runtime.
}
public void ManipulateBarWithoutRuntimeError()
{
bar = "Hello, world!";
bar.Substring(0, 1); // bar is actually set to a string object containing some text, so now the Substring method will succeed
}
}