了解对象范围基础知识
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
为例,你可以看到它只有数据成员(fullname
和cnt
),但是没有方法.尽管如此,您的程序确实对地址簿执行了多项操作,例如 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
。
我有一个关于对象的基本块:当你创建一个 "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
为例,你可以看到它只有数据成员(fullname
和cnt
),但是没有方法.尽管如此,您的程序确实对地址簿执行了多项操作,例如 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
。