簡體   English   中英

C#中的構造函數

[英]Constructors in C#

我已經閱讀了很多關於為什么構造函數有用以及我發現的所有資源都指定構造函數用於初始化類的實例的信息。 使用構造函數的一個主要好處是它可以保證對象在使用前經過適當的初始化,通常接受參數。 它有助於確保對象的完整性,並使使用面向對象語言編寫的應用程序更加可靠。

默認情況下,如果在類中未指定構造函數,則在 C# 中會實例化默認的空構造函數。

我發現的大多數示例都指定了這樣的內容;

public Car(int speedCurrent, int gearCurrent) {
    speed = speedCurrent;
    gear= startGear;
}

Car myCar = new Car(0, 0);

現在,當您可以指定屬性時,創建構造函數的實際意義是什么?

public int speed { get; set; }
public int gear { get; set; }

並像這樣初始化它;

Car myCar = new Car();
myCar.speed = 0;
myCar.gear = 0;

我無法滿足明確創建構造函數的需要。 如果有人能給我一個很好的實際例子,我將不勝感激。

雖然您可以像您顯示的那樣初始化屬性:

Car myCar = new Car();
myCar.speed = 0;
myCar.gear = 0;

如果沒有構造函數,您也可以選擇初始化任何屬性

Car myCar = new Car();

這可能會導致班級工作不佳/根本無法工作。

構造函數的目的是強制您在允許使用類實例之前初始化所有必需的屬性(按設計)。

如果您考慮在具有多個團隊成員的項目中工作的情況,這將特別有用。 如果您的團隊成員想要使用您的class ,通過擁有正確的構造函數,您已經可以向他們提示需要在class中初始化哪些屬性以便他們正確使用類。

現在,當您可以指定屬性時,創建構造函數的實際意義是什么?

擁有非默認構造函數的好處是您必須將所有必需的依賴項傳遞給它。 這保證了當實例化時,對象將擁有它正常運行所需的一切。

現在當您可以指定屬性時創建構造函數的實際意義是什么

公開屬性的方法有四個問題:

  • 初始化后無法確保一致性——例如,如果要確保非零速度需要非零檔位,則不再可以控制此檢查
  • 當必須以原子方式更新多個屬性時,您無法確保一致性- 在必須同時更新speedgear並發情況下,由鎖保護的獨立公開屬性的方法將不可靠
  • 你被迫使你的對象可變- 這並不總是可取的,特別是當你的對象是為並發使用而設計的
  • 您被迫公開用戶可能不需要的方法- 即使在您的用戶期望可變對象的情況下,也可能不希望公開單個 setter; 使用這種方法,您別無選擇。

我無法滿足明確創建構造函數的需要。

以下是當您的 setter 未公開時您可以做什么的簡短演示:

class Car {
    private readonly object lockObject = new object();
    private int speed;
    private int gear;
    public int Speed {
        lock (lockObject) {
            return speed;
        }
    }
    public int Gear {
        lock (lockObject) {
            return gear;
        }
    }
    public int Gear{ get; }
    public Car(int speed, int gear) {
        // You can validate two attributes together
        if (speed != 0 && gear == 0) {
            throw new ArgumentException("Non-zero speed in zero gear");
        }
        this.speed = speed;
        this.gear= gear;
    }
    public void SpeedUp(int increase) {
        lock(lockObject) {
            var newSpeed = Math.Max(Speed + increase, 0);
            if (newSpeed > 200) {
                 throw new InvalidOperationexception("Cannot speed up past 200");
            }
            // Concurrent users would not see inconsistent speed setting
            Speed = newSpeed;
            Gear = Speed / 25;
        }
    }
}

主要優點是您能夠在並發環境中同時進行調整,以及一種確保初始對象一致性的方法。

你的例子中的實際點是你不需要做一個new Car(0, 0); 用於實例化,而只是為默認初始化執行此操作:

public Car() {
    speed = 0;
    gear = 0;
}

Car myCar = new Car();

非默認構造函數強制您的類的客戶端提供參數。 代碼不需要通過屬性 a) 初始化 b) 可以公開尚未准備好使用的類。 此外,您可能想對其他人隱藏速度或齒輪屬性。

根據您的類的作用,您可能需要進行一些初始化。 例如,一個類可能會在初始化時鈎住一個靜態事件,或者輸出到調試......在編碼時它也給了你一個點,你可以設置一個斷點來在任何嘗試實例化類時中斷。

問題是 Car 類的用戶需要以某種方式知道他們需要設置汽車的初始速度和檔位。

我遵循的規則准則是構造函數應該創建一個 100% 可以使用的對象。

構造函數的概念很老派。 回到 C 和 C++ 時代,當您自己管理所有指針時。 構造函數將設置大部分可空變量,以確保您在執行代碼時不會遇到一般保護錯誤或分段錯誤。

現代編碼人員避免使用帶參數的構造函數。 這是由於它們在執行自動化單元測試時造成的困難。 使用只有默認構造函數(沒有參數的構造函數)的類並在事后調用屬性或方法來初始化它們是完全可以的。

我會說這歸結為您希望如何實現您正在構建的課程的目標。

在 OO Programming 中實現封裝,您只能通過 getter/setter 訪問成員。 同樣,這取決於您想在實現 OO 原則方面走多遠。

何時應該將變量傳遞給構造函數的一個例子是,如果構造函數中有一些需要這些值的初始化代碼。

private int speed { get; set; }
private int gear { get; set; }
private bool reduceSpeed { get; set; }

public Car(int speedCurrent, int gearCurrent) {
    speed = speedCurrent;
    gear= startGear;
    if (speed > 30)
        reduceSpeed = true; // do further processing with this.
    ...
}

C# 中的構造函數:(簡單的理論)

  • 是一種特殊類型的函數/方法,與它的類同名。
  • 每當創建類的對象時都會自動調用它。
  • 它還負責其類成員的對象初始化和內存分配。
  • 它沒有返回類型,甚至沒有空類型。
  • 支持使用參數化構造函數進行重載。

它們有 4 種類型:

在此處輸入圖片說明

默認構造函數:

默認構造函數沒有參數。 當一個類沒有構造函數時,編譯器會為該類提供默認構造函數。 如果您沒有定義任何構造函數,則默認構造函數由編譯器定義。 它用於為類的實例變量分配默認值

示例:

namespace Default_parameterizedConstructor
{
    public class Student
    {
        // instance members
        public int StudentId { get; set; }
        public string Name { get; set; }
        public string College { get; set; }

        //default constructor parameters less
        public Student()
        {
            College = "My College"; //explicit default value for college
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //Object instance of Student.
            Student st = new Student();
            Console.WriteLine("Student details are Student ID: {0}, Student Name: {1}, College :{2}", st.StudentId, st.Name, st.College);
            Console.ReadKey();
        }
    }
}

默認構造函數的輸出將是:“學生詳細信息是學生 ID:0,學生姓名:,學院:我的學院”

參數化構造函數:參數化構造函數具有一個或多個參數。 當我們必須傳遞一個或多個參數時使用,以便我們可以設置類實例成員的參數。 用於為類的實例變量賦值。 當實例成員名稱和構造函數接收參數相同時,我們使用 this 關鍵字,在這種情況下,必須指定 this 關鍵字。 如果名稱不同,則可以選擇使用此關鍵字。

現在,讓我們以參數化構造函數為例:

namespace Default_parameterizedConstructor
{

    public class Student
    {
        // instance members
        public int StudentId { get; set; }
        public string Name { get; set; }
        public string College { get; set; }

        //default constructor parameters less
        public Student()
        {
            College = "My College"; //explicit default value for college
        }
        // parameterised constructor
        public Student(int studentid, string name, string college)
        {
            this.StudentId = studentid;
            this.Name = name;
            this.College = college;

        }
        public Student(int studentid, string name)
        {
            this.StudentId = studentid;
            this.Name = name;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //default constructor parameter
            //Object instance of Student.
            Student st = new Student();
            Console.WriteLine("The output for Default Constructor are Student ID: {0}, Student Name: {1}, College :{2}", st.StudentId, st.Name, st.College);
            
            
            // parameterised constructor
            //Operator overloading happening.
            Student st1 = new Student(1, "John Doe", "MIT");
            Console.WriteLine("The output for Default Constructor are Student ID: {0}, Student Name: {1}, College :{2}", st1.StudentId, st1.Name, st1.College);

            // parameterised constructor with operator overloading
            Student st2 = new Student(1,"Jimmy");
            Console.WriteLine("The output for Default Constructor are Student ID: {0}, Student Name: {1}, College :{2}", st2.StudentId, st2.Name, st2.College);

            Console.ReadKey();
        }
    }
}

*輸出如下: //First

Default Constructor 的輸出是 Student ID: 0, Student Name: , College :My College

//第二

默認構造函數的輸出是學生 ID:1,學生姓名:John Doe,學院:MIT

//第三

默認構造函數的輸出是學生 ID:1,學生姓名:Jimmy,學院:*

靜態構造函數:一種特殊類型的構造函數,在創建類的第一個對象之前被調用。 用於初始化任何靜態字段,或執行只需要執行一次的特定操作。 一個類只能有一個靜態構造函數,並且它必須是一個默認構造函數,沒有訪問修飾符。

私有構造函數:限制類的外部實例化,但在嵌套類中,您可以創建此類的實例。 在 C# 1 X 中沒有靜態類,因此開發人員使用私有構造函數來防止類外部實例化。 用於實現單例模式,即類的單個實例。 一個類可以有多個私有構造函數和公共構造函數。

如果您不創建實例,您將如何訪問該值? 我們可以通過將類的成員設置為私有靜態來做到這一點。 使用類名只能使用靜態成員。

讓我們通過下面的例子來看看兩者:

namespace StaticAndPrivate
{
    public class Example
    {
        private static int Counter;

        //private constructor
        private Example()
        {
            Counter = 10;
        }

        //static constructor
        static Example()
        {
            Counter = 20;
        }

        //public constructor
        public Example(int counter)
        {
            Counter = Counter + counter;
        }

        public static int GetCounter()
        {
            return ++Counter;
        }

        public class NestedExample
        {
            public void Test()
            {
                //internal instance
                Example ex = new Example();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //external instance
            //Example ex = new Example();

            Example ex = new Example(10);
            Console.WriteLine("Counter : {0}", Example.GetCounter());

            Console.ReadKey();
        }
    }
}

希望這可以通過一個例子來解釋這個概念。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM