简体   繁体   中英

Understanding Object scope fundamentals

I have a fundamental block when it comes to objects: When you create a "new" object it replaces the prior one. If this is true, where to you put an object creation so that it does not get replaced in a repetitious call such as while or For loop?

I am working through a C# book and am trying to create an address book using a multidimensional array as my table. My problem is each time I create the "New" array, the prior data is lost...:-(

If I move the Addressbook Object to another location, the rest of the program can't find it. All the books on object oriented design leave me confused and frustrated. Hopefully you can shed some light on where I am going wrong in my thinking. Here is my program:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class Addressbook
{
    public string[,] fullname;
    public int cnt;
}

namespace ConsoleApp
{
    class MethodParams
    {
        public static void Main()
        {
            string myChoice;

            MethodParams mp = new MethodParams();
            do
            {                                
                // show menu and get input from user
                myChoice = mp.getChoice();

                // Make a decision based on the user's choice
                mp.makeDecision(myChoice);

                // Pause to allow the user to see the results
                Console.Write("press Enter key to continue...");
                Console.ReadLine();
                Console.WriteLine();
            } while (myChoice != "Q" && myChoice != "q"); // Keep going until the user wants to quit
        }
//*******************
        // show menu and get user's choice
        string getChoice()
        {
            string myChoice;
            // Print A Menu
            Console.WriteLine("My Address Book\n");
            Console.WriteLine("A - Add New Address");
            Console.WriteLine("D - Delete Address");
            Console.WriteLine("M - Modify Address");
            Console.WriteLine("V - View Addresses");
            Console.WriteLine("Q - Quit\n");
            Console.WriteLine("Choice (A,D,M,V,or Q): ");
            // Retrieve the user's choice
            myChoice = Console.ReadLine();
            return myChoice;
        }
//***********************
        // make decision
        void makeDecision(string myChoice)
        {
            Addressbook addrBk = new Addressbook();  //Create Addressbook Object
            addrBk.fullname = new string[10, 10];
            addrBk.fullname[0, 0] = "Tom";
            addrBk.fullname[0, 1] = "Nesler";
            addrBk.cnt = 1;
            switch (myChoice)
            {
                case "A":
                case "a":
                    Console.WriteLine("Enter First name");
            String FName;
                    FName =  Console.ReadLine();
            Console.WriteLine("Enter Last name");
                    String LName;
                    LName =  Console.ReadLine();
                    addrBk.fullname[addrBk.cnt,0] = FName;
                    addrBk.fullname[addrBk.cnt,1] = LName;
                    this.addAddress(ref addrBk);        //Input address name
                    addrBk.cnt = addrBk.cnt + 1;
                    break;

                case "V":
                case "v":
                    this.viewAddresses(ref addrBk);
                    break;
                case "Q":
                case "q":
                    Console.WriteLine("Bye.");
                    break;
                default:
                    Console.WriteLine("{0} is not a valid choice", myChoice);
                    break;
            }
        }
//*****************
        // insert an address
        void addAddress(ref Addressbook addrBk)  //Addr Object containing name and Address
        {
            Console.WriteLine("Name: {0} {1} added.", addrBk.fullname[addrBk.cnt, 0], addrBk.fullname[addrBk.cnt, 1]);
        }
//*****************
        // show addresses
        void viewAddresses(ref Addressbook addrBk)
        {
            Console.WriteLine("count is: {0}", addrBk.cnt);
           for (int i=0; i < addrBk.cnt; i++) 
            {
               Console.WriteLine("counter = {0}",i );
               Console.WriteLine("Name: {0} {1} ", addrBk.fullname[i,0], addrBk.fullname[i,1] );
            }
        }
    }

}
string a;   // this is a declaration - the variable `a` is null/nothing until 
            // you assign it a value

a = new String("Hello world");  // the variable `a` now references a String 
                                // Object that contains the string "Hello world"

a = new String("Good-bye World");  // the variable `a` now references a String 
                                   // Object that contains the string "Good-bye World" 

This is an example of how you can lose an object. In the "old days" this was called a memory leak. In today's world of managed code, the lost object is picked up by "garbage collection"

When you instantiate and object, usually you pass it off to some collection of like objects for later reference. Maybe a List(of Address) or a Dictionary(of String, Address). this is where the object "lives" until the collection itself goes out of scope.

Inside of your program all of this code has a limited lifetime that is the run time of your code. When your program ends, that is the final scope of an objects lifetime. This is why we have things like databases so we can save and restore the ideas we create with programming languages

I assume you want to have your addrBk created and then updated, so You have several options in your example. First of all you can create your object inside Main method and then pass it to makeDecision method:

public static void Main()
{
///...
    Addressbook addrBk = new Addressbook();
    addrBk.fullname = new string[10, 10];
    addrBk.fullname[0, 0] = "Tom";
    addrBk.fullname[0, 1] = "Nesler";
    addrBk.cnt = 1;

    do
    {
        // rest of code
        mp.makeDecision(addrBk, myChoice);
        // rest of code
    } //while ...
}
    void makeDecision(Addressbook addrBk, string myChoice)
    {
        switch (myChoice)
        //rest of code...
    }

You can also create static object in your class and then use it as in your example you probably don't need to create more than one object. Check also other fnostro answer as it may lead you to more understanding of fundamentals.

Looks like you're creating a new Addressbook object each time the makeDecision() function is called. Furthermore, the only reference you're keeping to the Addressbook object is via a local variable within this function (addrBk). Thus, when the makeDecision function exits, your code will no longer have any way of finding the address book.

As for a solution to this particular piece of code...there are several solutions. The quickest solution (but not necessarily cleanest overall) would be to take the first 5 lines of your makeDecision() function and put them right at the beginning of the Main() function. Then simply pass the addrBk variable into the makeDecision() function as a parameter each time your call it.

One of the things that you may be confused about relates to member variables. One of the fundamental ideas is that an object encapsulates the data it operates on together with the methods. This means, that this data is available to all of the methods in an object, and don't have to be supplied as arguments to the method. This data is stored in member variables.

Taking your AddressBook example, you can see that it only has data members ( fullname and cnt ), but no methods. Still, your program does perform several operations on an addressbook, like addAddress to add an address to it and viewAddresses to show all the addresses in the address book on the console.

What would be typical for a class is that it contains both the data and the methods. So in your case (just moving some of your existing code around, and applying some idiomatic c# tweeks to naming):

public class Addressbook
{
    private string[,] _fullname;
    private int _cnt;

    public Addressbook()
    {
       // Questions:
       // Are 10 fields really needed for firstName and lastName?
       // What if a user adds more than 10 names.
        _fullname = new string[10, 10]; 
    }

    public void AddAddress(string firstName, string lastName)
    {
        _fullname[_cnt,0] = firstName;
        _fullname[_cnt,1] = lastName;
        Console.WriteLine("Name: {0} {1} added.", _fullname[_cnt, 0], _fullname[_cnt, 1]);
        _cnt = _cnt + 1;
    }

    public void ViewAddresses()
    {
        Console.WriteLine("count is: {0}", _cnt);
        for (int i=0; i < _cnt; i++) 
        {
           Console.WriteLine("counter = {0}",i );
           Console.WriteLine("Name: {0} {1} ", _fullname[i,0], _fullname[i,1] );
        }
    }
}

The AddressBook class now can do everything it could before, and now MethodParams can use it without knowing exactly how the AddressBook does its thing. It just needs to know what can be done.

Now if we change the class MethodParams a bit so that we can give it an addressbook to work with, things become a lot easier:

class MethodParams
{
    private AddressBook _addressBook;

    public MethodParams(AddressBook addressBook)
    {
        _addressBook = addressBook; // this is the address book we will work with
    }

    public static void Main()
    {
        string myChoice;
        AddressBook theAddressBook = new AddressBook();

        MethodParams mp = new MethodParams(theAddressBook);
        do
        {                      
             // ... this can all stay the same.          
        } while (myChoice != "Q" && myChoice != "q"); // Keep going until the user wants to quit

        // When we get here, theAddressBook still contains everything.
        // We could save it to a file.
    }

    // ... see remarks for changes below.
}

Now in your makeDecision method you can do the following, to add an address:

_addressBook.AddAddress(FName, LName);

And this to view all addresses.

_addressBook.ViewAddresses();

I hope this helps clarify a few things. Here are some answers to your questions in the comments:

This line public class Addressbook starts the definition of a class , which is a type , just like string is a type.

This line private AddressBook _addressBook; declares a private member variable named _addressBook that is of the type AddressBook and belongs to the class MethodParams .

In this line AddressBook theAddressBook = new AddressBook(); we create a new object instance of type AddressBook that is assigned to the local variable with the name theAddressBook .

In this line MethodParams mp = new MethodParams(theAddressBook); we create a new object instance of the type MethodParams and initialize it through its constructor method public MethodParams(AddressBook addressBook) , passing it a reference to the object instance named theAddressBook as the method argument for the method parameter named addressBook .

In this line _addressBook = addressBook , the value supplied as addressBook to the constructor (which is a reference to the theAddressBook object instance that we created earlier in this code), is assigned to the private member variable named _addressBook . Now when any of the methods in MethodParams use _addressBook , eg like this _addressBook.AddAddress(FName, LName) they are working with the object instance theAddressBook that we created.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM