-2

My C# program needs to receive xml data and store it in sql database. I have Batches which has many Instructions. I implement the classes thusly:

class Batch
{
    public string batchUID { get; set; }
    public Instruction[] instructionArray { get; set; }
}

class Instruction
{
    public string instructionUID { get; set; }
}

My program runs like this

Batch myBatch = new Batch();
myBatch.instructionArray = new Instruction[3];
myBatch.instructionArray[0] = new Instruction();
SomeFunc(myBatch.instructionArray[0]);

public void SomeFunc(Instruction instruction)
{
    // How do I do the following?
    console.write( instruction.OWNER.batchUID )
}

I have tried to search for this extensively but every result is related to inheritance, inner/outer classes, etc. I would like to avoid creating a batchUID method inside class Instruction if possible.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • 1
    `new Instruction[3]` just sizes the array, all values will be `null`, there are no `Instruction` instances in there. You have to create them, and the good moment there is that you pass the parent as a parameter to the constructor of `Instruction`. – Ray Jul 20 '16 at 17:00
  • You have to somehow set your instructions. `myBatch.instructionArray[2] = new Instruction { instructionUid = "foo" };` – Chase Florell Jul 20 '16 at 17:03
  • whether i instantiate myBatch.instructionArray[n] or not makes no difference to my question... – p4ndepravity Jul 20 '16 at 20:06
  • @p4ndepravity The code sample you provided however has a serious bug and will just cause a `NullReferenceException`. So I had to point it out before you run into trouble there. – Ray Jul 21 '16 at 11:56

2 Answers2

0

Your code has an issue:

myBatch.instructionArray = new Instruction[3];

That will just create an array with 3 null values, not 3 instances of Instruction. This is because a class is a reference type and it will not be instantiated automatically here (structs however are, since they are value types and get all their values initialized in a default constructor which is then called). You still have to create those instructions before passing them to your method as you'd effectively just be passing null.

Then, when creating an instance of Instruction, just pass the parent as a parameter, and remember it in the class, like this:

class Instruction
{
    public Instruction(Batch parent)
    {
        Parent = parent;
    }

    public Batch Parent { get; private set; }
    public string InstructionUID { get; set; } // Please start properties with an uppercase letter
}

This way, you can access the Batch instance to which the Instruction instance belongs later on.

Batch myBatch = new Batch();
myBatch.InstructionArray = new Instruction[3];
// TODO: Create the instances here, the array is just null null null now!
myBatch.InstructionArray[0] = new Instance(myBatch); // example
SomeFunc(myBatch.InstructionArray[0]);

public void SomeFunc(Instruction instruction)
{
    Console.Write(instruction.Parent.BatchUID)
}

If this is good design is another question, the Instruction might have the SomeFunc stuff inside itself, but I'm not sure what the project should achieve eventually.

Ray
  • 7,940
  • 7
  • 58
  • 90
0

You can pass an instance of Batch to the constructor of Instruction, and then store a reference to that Batch that you can expose with a property. (This was already shown in another answer - I'm repeating it because it provides context to what I'm going to add next.)

class Instruction
{
    public Instruction(Batch parent)
    {
        Parent = parent;
    }

    public Batch Parent { get; private set; }
    public string InstructionUID { get; set; } 
}

Now Instruction has a Parent property that returns a Batch.

But there's a gap. If you call

var parent = batch.Instruction[0].Parent

is parent == batch? It looks like that's the intent. The Instruction contains a reference to the Batch that contains it.

But nothing is enforcing that. For example, I could do this:

someBatch.Instruction[0] = someOtherBatch.Instruction[1];

Now someBatch contains an array of Instruction, but at least one of them actually has someOtherBatch as its parent.

Maybe that possibility is no big deal, but I think that if Parent is meant to refer to the Batch containing the Instruction but it might not, then you haven't actually accomplished what you're aiming for.

I'd recommend creating a separate class that contains both a Batch and an Instruction. (Perhaps ParentInstructionRelation?)

public class ParentInstructionRelation
{
    Batch Parent {get;private set;}
    Instruction Instruction {get;private set;}

    public ParentInstructionRelation(Batch parent, Instruction instruction)
    {
        Parent = parent;
        Instruction = instruction;
    }
}

That way Instruction doesn't need a reference to its parent. It probably shouldn't have a reference to its parent. What if the same Instruction is in two different instances of Batch? Which one is the parent?

But if Batch needs to expose an Instruction and a reference to itself, then it can do that by returning a ParentInstructionRelation. Or a class that reads the Instruction from Batch can create that relationship.

var instructions = batch.InstructionArray
    .Select(instruction => new ParentInstructionRelation(batch, instruction));
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • this was the closest to what I was looking for and also didn't bother explaining that i need to instantiate the variable. thanks Scott – p4ndepravity Jul 20 '16 at 20:10