我是C#初学者。我发现有两种方法可以编写代码并输出相同的结果。 你能解释一下它们之间的区别吗?何时使用#1和#2?
#1
class Program
{
static void Main()
{
Program min = new Program();
Console.WriteLine(min.isMin(1, 2));
Console.ReadLine();
}
int isMin(int value1, int value2)
{
int Min;
return Min = Math.Min(value1, value2);
}
}
#2
class Program2
{
static void Main()
{
Console.WriteLine(isMin(1, 2));
Console.ReadLine();
}
static int isMin(int value1, int value2)
{
int Min;
return Min = Math.Min(value1, value2);
}
}
答案 0 :(得分:14)
#1和#2之间的区别在于#1中,isMin是类Program的实例成员函数,因此您必须创建Program类的实例
Program min = new Program()
然后才调用实例成员函数isMin:
min.isMin(..)
在#2中,isMin是Program类的静态成员函数,由于Main也是同一个类的静态成员函数,因此可以从Main函数直接调用isMin。
两者都有效。静态函数Main是程序的“入口点”,这意味着它首先被执行。其余的只是面向对象的语义。
修改强>
似乎为了更好地说明这一点,一个例子将是有序的。
除了显示将程序逻辑封装到对象之间的差异以及替代使用静态函数之外,下面的两个程序是非常无用的。
该程序定义了两个操作,并将处理两个数字(示例中为10和25)。程序运行时,它会将其操作跟踪到日志文件(每个数字一个)。可以想象,这两个操作可以被更严格的算法所取代,并且这两个数字可以被一系列更有用的输入数据所取代。
//The instance-based version:
class Program
{
private System.IO.StreamWriter _logStream;
private int _originalNumber;
private int _currentNumber;
public Program(int number, string logFilePath)
{
_originalNumber = number;
_currentNumber = number;
try
{
_logStream = new System.IO.StreamWriter(logFilePath, true);
_logStream.WriteLine("Starting Program for {0}", _originalNumber);
}
catch
{
_logStream = null;
}
}
public void Add(int operand)
{
if (_logStream != null)
_logStream.WriteLine("For {0}: Adding {1} to {2}", _originalNumber, operand, _currentNumber);
_currentNumber += operand;
}
public void Subtract(int operand)
{
if (_logStream != null)
_logStream.WriteLine("For {0}: Subtracting {1} from {2}", _originalNumber, operand, _currentNumber);
_currentNumber -= operand;
}
public void Finish()
{
Console.WriteLine("Program finished. {0} --> {1}", _originalNumber, _currentNumber);
if (_logStream != null)
{
_logStream.WriteLine("Program finished. {0} --> {1}", _originalNumber, _currentNumber);
_logStream.Close();
_logStream = null;
}
}
static void Main(string[] args)
{
Program p = new Program(10, "log-for-10.txt");
Program q = new Program(25, "log-for-25.txt");
p.Add(3); // p._currentNumber = p._currentNumber + 3;
p.Subtract(7); // p._currentNumber = p._currentNumber - 7;
q.Add(15); // q._currentNumber = q._currentNumber + 15;
q.Subtract(20); // q._currentNumber = q._currentNumber - 20;
q.Subtract(3); // q._currentNumber = q._currentNumber - 3;
p.Finish(); // display original number and final result for p
q.Finish(); // display original number and final result for q
}
}
以下是基于静态函数的同一程序的实现。注意我们如何“进入状态”进出每个操作,以及Main函数如何“记住”哪个数据与哪个函数调用有关。
class Program
{
private static int Add(int number, int operand, int originalNumber, System.IO.StreamWriter logFile)
{
if (logFile != null)
logFile.WriteLine("For {0}: Adding {1} to {2}", originalNumber, operand, number);
return (number + operand);
}
private static int Subtract(int number, int operand, int originalNumber, System.IO.StreamWriter logFile)
{
if (logFile != null)
logFile.WriteLine("For {0}: Subtracting {1} from {2}", originalNumber, operand, number);
return (number - operand);
}
private static void Finish(int number, int originalNumber, System.IO.StreamWriter logFile)
{
Console.WriteLine("Program finished. {0} --> {1}", originalNumber, number);
if (logFile != null)
{
logFile.WriteLine("Program finished. {0} --> {1}", originalNumber, number);
logFile.Close();
logFile = null;
}
}
static void Main(string[] args)
{
int pNumber = 10;
int pCurrentNumber = 10;
System.IO.StreamWriter pLogFile;
int qNumber = 25;
int qCurrentNumber = 25;
System.IO.StreamWriter qLogFile;
pLogFile = new System.IO.StreamWriter("log-for-10.txt", true);
pLogFile.WriteLine("Starting Program for {0}", pNumber);
qLogFile = new System.IO.StreamWriter("log-for-25.txt", true);
qLogFile.WriteLine("Starting Program for {0}", qNumber);
pCurrentNumber = Program.Add(pCurrentNumber, 3, pNumber, pLogFile);
pCurrentNumber = Program.Subtract(pCurrentNumber, 7, pNumber, pLogFile);
qCurrentNumber = Program.Add(qCurrentNumber, 15, qNumber, qLogFile);
qCurrentNumber = Program.Subtract(qCurrentNumber, 20, qNumber, qLogFile);
qCurrentNumber = Program.Subtract(qCurrentNumber, 3, qNumber, qLogFile);
Program.Finish(pCurrentNumber, pNumber, pLogFile);
Program.Finish(qCurrentNumber, qNumber, qLogFile);
}
}
另一点值得注意的是,尽管基于第一个实例的示例有效,但在实践中将逻辑封装在可以在程序的主入口点中使用的不同类中更为常见。这种方法更加灵活,因为它使您可以非常轻松地获取程序逻辑并将其移动到不同的文件,甚至可以移动到甚至可以被多个应用程序使用的不同程序集。这是一种方法。
// Another instance-based approach
class ProgramLogic
{
private System.IO.StreamWriter _logStream;
private int _originalNumber;
private int _currentNumber;
public ProgramLogic(int number, string logFilePath)
{
_originalNumber = number;
_currentNumber = number;
try
{
_logStream = new System.IO.StreamWriter(logFilePath, true);
_logStream.WriteLine("Starting Program for {0}", _originalNumber);
}
catch
{
_logStream = null;
}
}
public void Add(int operand)
{
if (_logStream != null)
_logStream.WriteLine("For {0}: Adding {1} to {2}", _originalNumber, operand, _currentNumber);
_currentNumber += operand;
}
public void Subtract(int operand)
{
if (_logStream != null)
_logStream.WriteLine("For {0}: Subtracting {1} from {2}", _originalNumber, operand, _currentNumber);
_currentNumber -= operand;
}
public void Finish()
{
Console.WriteLine("Program finished. {0} --> {1}", _originalNumber, _currentNumber);
if (_logStream != null)
{
_logStream.WriteLine("Program finished. {0} --> {1}", _originalNumber, _currentNumber);
_logStream.Close();
_logStream = null;
}
}
}
class Program
{
static void Main(string[] args)
{
ProgramLogic p = new ProgramLogic(10, "log-for-10.txt");
ProgramLogic q = new ProgramLogic(25, "log-for-25.txt");
p.Add(3); // p._number = p._number + 3;
p.Subtract(7); // p._number = p._number - 7;
q.Add(15); // q._number = q._number + 15;
q.Subtract(20); // q._number = q._number - 20;
q.Subtract(3); // q._number = q._number - 3;
p.Finish();
q.Finish();
}
}
答案 1 :(得分:5)
这里的关键区别是基于面向对象的编程。 C#是一种面向对象的语言,其中的大部分内容都是对象。在这种情况下,程序是一个对象。它很特殊,因为它具有静态void Main()函数,即程序的“入口点”。
差异来自isMin函数的静态修饰符。类定义对象的工作方式。当你实际创建一个时,就像你使用新的Program()一样,你已经'实例化',或者制作了该对象的实际工作副本。
通常这样做是为了跟踪属于该对象的一组变量,但在这种情况下没有这样的变量。
在第一种情况下,您正在创建对象程序的实例,并告诉它在该实例上执行其“isMin”函数。在第二种情况下,您没有创建任何实例,并且您告诉它执行与该类关联的“isMin”函数(而不是该对象的实例)。除了一些更简单的语法之外,这里没有真正的区别,因为在对象中没有跟踪数据。
当您拥有对象数据时,您会发现这很重要,因为当您处于静态函数时,您将无法访问“实例”数据。要了解更多信息,请研究面向对象的编程。
答案 2 :(得分:3)
这两种方法通过实例方法(在第一种方法中)和静态方法(在第二种方法中)来区分。第一个声明Program
类的实例,然后在此实例上调用isMin
方法,而第二个只调用isMin
方法而不引用任何实例(您可以认为该方法总是属于整个类,并在此一般上下文中执行)。如果您来自BASIC背景,则静态方法基本上类似于模块中定义的方法。
就最佳实践而言,第二种方法是可供选择的方法。有一个Program
类的实例没有明确的理由(考虑到你在同一个程序集/进程中永远不会有多个),为什么还要费心呢?更有意义的是isMin
方法应该是静态的,即不依赖于甚至与任何特定实例(或任何特定类甚至相关)相关 - 您也可以在另一个名为MathHelper
的类中定义它。
希望能为你澄清事情。
答案 3 :(得分:0)
两者之间的区别在于第一个使用实例方法,第二个使用静态方法。实例方法是与给定类的每个对象相关联的方法。静态方法与类本身相关联。在第一种情况下,您必须创建类的对象,然后您可以使用与该特定对象关联的方法。在第二种情况下,您可以仅使用基于类的方法。通常,您将为方法添加类名前缀,但由于您所在的范围是类,因此静态方法的类假定为当前类。这解释了差异在很高的水平。
更重要的是你如何使用这些方法编写程序。在面向对象的编程中,您通常认为对象包含一些数据以及对该数据进行操作的操作(方法)。从某种意义上说,它是独立的。编写OO程序的典型方法是使用具有实例方法的类。静态方法通常很少见,至少相对较少。
为什么会这样?好吧,静态方法必须从参数中获取所有数据,因为它们与任何特定对象都没有关联。请记住,对象由数据AND操作组成。有时我们有适用的方法。 Math类就是一个很好的例子。通常,它的方法是静态的,并采用一个或两个参数并产生结果。不需要对象,因为所需的唯一数据是方法的参数,创建一个类来保存两个参数然后只对它们执行一次操作是没有意义的。
但是,通常情况下,我们的班级数据更复杂,对它们的各种操作并不那么简单和多样。在这种情况下,让方法对对象的数据进行操作而不是通过参数传递所有数据是有意义的。在这种情况下,我们需要一个类的特定实例,以便我们可以为操作提供特定的数据。
正如Brian Marick所说,现在是一个好榜样。
假设您有一个代表汽车的课程,并且正在模拟许多汽车在城镇中行驶。汽车有许多潜在的操作:启动,加速,转弯,制动等。汽车也会有几个属性可能影响这些操作的各种算法:引擎,BrakePackage,SteeringPackage等。不是每辆车都有相同的Engine,BrakePackage和SteeringPackage的值。
使用实例方法,我们会让方法:Start,Accelerate,Turn,Brake对对象本身包含的数据进行操作。
public class Car
{
public Engine Engine { get; set; }
public BrakePackage Brakes { get; set; }
public SteeringPackage Steering { get; set; }
public double X { get; private set; }
public double Y { get; private set; }
public double Z { get; private set; }
public void Accelerate( double pedalPressure )
{
this.Engine.MoveThrottle( pedalPressure, UpdatePosition );
}
public void UpdatePosition( double x, double y, double z, int deltaTime )
{
this.CalculateSpeed( this.X, this.Y, this.Z, x, y, z, deltaTime );
this.X = x;
this.Y = y;
this.Z = z;
}
...
}
另一方面,如果我们使用静态方法,那么我们必须将所有各种参数传递给方法,包括我们想要更新的那些值,因为这些方法不会与任何特定数据相关联。这实际上很痛苦,我甚至不想输出与上述相同的例子。
从某种意义上说,不幸的是你需要开始使用静态Main方法编写一个程序,因为它可以让你开始走错路。你想要做的是开始考虑你的对象 - 他们需要什么数据以及你将对它们执行什么操作。每隔一段时间你就会发现你有一个适用于类的所有对象的操作,不需要访问任何特定对象中的任何或大部分数据,或者只处理有关类本身的信息。这些对象将成为静态方法的候选对象。但是,大多数方法都适合您的第一个示例,而不是您的第二个示例。