Что означает статическое ключевое слово?

Я начинаю С#. Я обнаружил, что есть 2 способа писать коды и выводить те же результаты. Не могли бы вы объяснить разные между ними? А когда использовать # 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);
    }
}

Ответ 1

Разница между # 1 и # 2 заключается в том, что в # 1 isMin является функцией члена экземпляра класса Program, поэтому вам нужно создать экземпляр класса Program

 Program min = new Program()

и только затем вызвать функцию-член экземпляра isMin:

 min.isMin(..)


В # 2 isMin является статической функцией-членом класса Program, и поскольку 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
    }
}

Ниже приведена статическая функция, основанная на реализации одной и той же программы. Обратите внимание на то, как мы должны "переносить наше состояние" в каждую операцию и выходить из нее, и как основная функция должна "помнить", какие данные идут с вызовом функции.

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

Ответ 2

Основное различие здесь основано на объектно-ориентированном программировании. С# - объектно-ориентированный язык, и большинство вещей в нем - это объекты. В этом случае программа является объектом. Это особенность, потому что она имеет функцию static void Main(), которая является "точкой входа" для программы.

Разница возникает из-за статического модификатора функции isMin. Классы определяют, как работают объекты. Когда вы делаете это, как и в случае с новой программой(), вы создаете "экземпляр" или создаете фактическую рабочую копию этого объекта.

Обычно это делается для отслеживания набора переменных, которые являются частью этого объекта, но в этом случае таких переменных нет.

В первом случае вы делаете экземпляр объектной программы и говорите ей, чтобы она выполняла свою функцию "isMin" в этом экземпляре. Во втором случае вы не делаете никаких экземпляров, и вы говорите ему, чтобы выполнить функцию isMin, связанную с классом (а не экземпляр объекта). Здесь нет никакой реальной разницы, кроме некоторого упрощенного синтаксиса, поскольку в объекте не отслеживаются данные.

Вы обнаружите, что это имеет значение, когда у вас есть данные об объектах, потому что вы не сможете получить доступ к данным экземпляра, когда находитесь в статической функции. Чтобы понять больше, изучите объектно-ориентированное программирование.

Ответ 3

Два метода различаются методом экземпляра (в первом) и статическим методом (во втором). Первый объявляет экземпляр класса Program, а затем вызывает метод isMin в этом экземпляре, тогда как второй просто вызывает метод isMin без ссылки на какой-либо экземпляр (вы можете думать, что метод всегда принадлежит класс в целом и выполняется в этом общем контексте). Если вы исходите из фона BASIC, статический метод по существу похож на метод, определенный в модуле.

Что касается лучших практик, то второй метод выбирается одним. Нет явной причины иметь экземпляр класса Program (считайте, что у вас никогда не будет больше одного в той же сборке/процессе), так зачем беспокоиться? Имеет смысл, что метод isMin должен быть статическим, т.е. Не зависящим от какого-либо конкретного экземпляра (или какого-либо конкретного класса четным) или даже связанным с ним, вы также можете определить его в другом классе MathHelper).

Надеюсь, что это разъяснит вам.

Ответ 4

Разница между ними заключается в том, что первая использует методы экземпляра, а вторая использует статические методы. Метод экземпляра - это метод, который связан с каждым объектом данного класса. Статический метод связан с самим классом. В первом случае вам нужно создать объект класса, тогда вы можете использовать метод, связанный с этим конкретным объектом. Во втором случае вы можете использовать метод, основанный только на классе. Как правило, вы должны префикс метода с именем класса, но поскольку область, в которой вы находитесь, является классом, класс статического метода считается текущим классом. Это объясняет, какая разница на очень высоком уровне.

Чем важнее то, как вы на самом деле собираетесь писать программу, используя эти методы. В объектно-ориентированном программировании вы обычно считаете объект содержащим некоторые данные и операции (методы), которые работают с этими данными. В некотором смысле, это самодостаточно. Типичным способом написания программ OO является использование классов с методами экземпляра. Статические методы обычно бывают редкими, по крайней мере сравнительно редко.

Зачем это было? Ну, статические методы должны получить все свои данные из своих аргументов, поскольку они не связаны с каким-либо конкретным объектом. Помните, что объекты состоят из операций с данными И. Иногда у нас есть методы, для которых это работает. Хорошим примером является класс Math. Обычно методы статичны и принимают один или два аргумента и дают результат. Нет необходимости в объекте, так как единственными необходимыми данными являются аргументы метода, и было бы бессмысленно создавать класс для хранения двух аргументов, а затем выполнять только одну операцию.

Как правило, данные нашего класса более сложны, и различные операции над ними не так просты и многочисленны. В этом случае имеет смысл использовать методы для данных объекта, а не передавать все эти данные через параметры. В этом случае нам нужны конкретные экземпляры класса, так что мы можем иметь конкретные данные для операций, которые будут работать.

Как говорит Брайан Марик, сейчас самое подходящее время для примера.

Скажем, у вас был класс, который представлял собой автомобиль, и проводил симуляцию многих автомобилей, едущих по городу. У автомобилей есть много потенциальных операций: запуск, ускорение, поворот, торможение и т.д. У автомобиля также будет несколько свойств, которые могут повлиять на различные алгоритмы для этих операций: двигатель, BrakePackage, SteeringPackage и т.д. Не каждый автомобиль будет иметь одинаковые значения для двигателя, 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, потому что он может заставить вас начать с неправильной ноги. То, что вы хотите сделать, - это начать думать о своих объектах - какие данные им нужны и какие операции вы будете выполнять над ними. Время от времени вы обнаружите, что у вас есть операция, которая применяется ко всем объектам класса и не нуждается в доступе к какой-либо или большей части данных в каком-либо конкретном объекте или касается только информации о самом классе. Эти объекты будут кандидатами на статические методы. Однако большинство ваших методов будут соответствовать шаблону вашего первого примера, а не вашей второй.