了解对象范围基础知识

Understanding Object scope fundamentals

我有一个关于对象的基本块:当你创建一个 "new" 对象时,它会替换之前的对象。如果这是真的,您将对象创建放在哪里,以便它不会在重复调用(例如 while 或 For 循环)中被替换?

我正在研究一本 C# 书籍,并尝试使用多维数组作为我的 table 创建地址簿。我的问题是每次创建 "New" 数组时,先前的数据都会丢失...:-(

如果我将地址簿对象移动到另一个位置,程序的其余部分将无法找到它。所有关于面向对象设计的书籍都让我感到困惑和沮丧。希望您能阐明我的想法哪里出了问题。 这是我的程序:

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" 

这是一个如何丢失对象的示例。在 "old days" 中,这被称为内存泄漏。在当今的托管代码世界中,丢失的对象由 "garbage collection"

捡起

当您实例化对象时,通常会将其传递给一些类似对象的集合以供以后参考。可能是一个列表(地址)或一个字典(字符串,地址)。这是对象所在的位置 "lives",直到集合本身超出范围。

在您的程序内部,所有这些代码的生命周期都是有限的,即代码的 运行 时间。当您的程序结束时,这就是对象生命周期的最终范围。这就是为什么我们有数据库之类的东西,这样我们就可以保存和恢复我们用编程语言创造的想法

我假设您想创建并更新 addrBk,所以 您的示例中有多个选项。首先,您可以在 Main 方法中创建对象,然后将其传递给 makeDecision 方法:

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...
    }

您还可以在 class 中创建静态对象,然后在您的示例中使用它,您可能不需要创建多个对象。 还要检查其他 fnostro 答案,因为它可能会让您对基础知识有更多的了解。

看起来您在每次调用 makeDecision() 函数时都在创建一个新的地址簿对象。此外,您对 Addressbook 对象的唯一引用是通过此函数 (addrBk) 中的局部变量。因此,当 makeDecision 函数退出时,您的代码将无法再找到地址簿。

至于针对这段特定代码的解决方案...有多种解决方案。最快的解决方案(但不一定是整体上最干净的)是将 makeDecision() 函数的前 5 行放在 Main() 函数的开头。然后在每次调用时将 addrBk 变量作为参数传递给 makeDecision() 函数。

您可能会感到困惑的一件事与成员变量有关。其中一个基本思想是对象将它操作的数据与方法一起封装起来。这意味着,此数据可用于对象中的所有方法,并且不必作为参数提供给方法。此数据存储在成员变量中。

以你的AddressBook为例,你可以看到它只有数据成员(fullnamecnt),但是没有方法.尽管如此,您的程序确实对地址簿执行了多项操作,例如 addAddress 向其添加地址和 viewAddresses 以在控制台上显示地址簿中的所有地址。

class 的典型情况是它既包含数据又包含方法。所以在你的情况下(只是移动一些现有的代码,并应用一些惯用的 c# tweeks 来命名):

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] );
        }
    }
}

AddressBook class 现在可以做它以前能做的一切,现在 MethodParams 可以在不知道 如何 的情况下使用它AddressBook 做它的事情。它只需要知道可以做什么

现在,如果我们稍微更改 class MethodParams 以便我们可以给它一个地址簿来使用,事情就会变得容易得多:

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.
}

现在,在您的 makeDecision 方法中,您可以执行以下操作来添加地址:

_addressBook.AddAddress(FName, LName);

然后查看所有地址。

_addressBook.ViewAddresses();

我希望这有助于澄清一些事情。以下是评论中对您问题的一些回答:

这一行public class Addressbook开始定义一个class,这是一个类型,就像string 是一种类型。

这一行private AddressBook _addressBook;声明了一个名为_addressBook的私有成员变量,属于AddressBook类型,属于[=92] =] MethodParams.

在这一行 AddressBook theAddressBook = new AddressBook(); 中,我们创建了一个类型为 AddressBook 的新 对象实例 ,并将其分配给名称为 [=32= 的局部变量].

在这一行MethodParams mp = new MethodParams(theAddressBook);中,我们创建了一个类型为MethodParams的新对象实例,并通过其构造函数方法对其进行了初始化public MethodParams(AddressBook addressBook),将对名为 theAddressBook 的对象实例的引用作为 方法参数 传递给名为 addressBook 方法参数

在这一行 _addressBook = addressBook 中,作为 addressBook 提供给构造函数的值(这是对我们在此代码前面创建的 theAddressBook 对象实例的引用)是分配给名为 _addressBook 的私有成员变量。现在,当 MethodParams 中的任何方法使用 _addressBook 时,例如像这样 _addressBook.AddAddress(FName, LName) 他们正在使用我们创建的对象实例 theAddressBook