it-swarm-vi.com

Xác thực tham số của nhà xây dựng trong C # - Thực tiễn tốt nhất

Thực hành tốt nhất để xác nhận tham số constructor là gì?

Giả sử một chút đơn giản của C #:

public class MyClass
{
    public MyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
            throw new ArgumentException("Text cannot be empty");

        // continue with normal construction
    }
}

Nó có thể được chấp nhận để ném một ngoại lệ?

Sự thay thế tôi gặp phải là xác nhận trước, trước khi bắt đầu:

public class CallingClass
{
    public MyClass MakeMyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
        {
            MessageBox.Show("Text cannot be empty");
            return null;
        }
        else
        {
            return new MyClass(text);
        }
    }
}
35
MPelletier

Tôi có xu hướng thực hiện tất cả các xác nhận của tôi trong hàm tạo. Đây là điều bắt buộc bởi vì tôi hầu như luôn tạo ra những vật thể bất biến. Đối với trường hợp cụ thể của bạn tôi nghĩ rằng điều này là chấp nhận được.

if (string.IsNullOrEmpty(text))
    throw new ArgumentException("message", "text");

Nếu bạn đang sử dụng .NET 4, bạn có thể làm điều này. Tất nhiên điều này phụ thuộc vào việc bạn coi một chuỗi chỉ chứa khoảng trắng là không hợp lệ.

if (string.IsNullOrWhiteSpace(text))
    throw new ArgumentException("message", "text");
27
ChaosPandion

Rất nhiều người nói rằng các nhà xây dựng không nên ném ngoại lệ. KyleG trên trang này , ví dụ, chỉ có thế. Thành thật mà nói, tôi không thể nghĩ ra lý do tại sao không.

Trong C++, ném ngoại lệ từ hàm tạo là một ý tưởng tồi, bởi vì nó để lại cho bạn bộ nhớ được phân bổ có chứa một đối tượng chưa được khởi tạo mà bạn không có tham chiếu nào (ví dụ: đó là rò rỉ bộ nhớ cổ điển). Đó có lẽ là nơi mà sự kỳ thị xuất phát - một nhóm các nhà phát triển C++ trường học cũ đã nửa chừng học hỏi C # của họ và chỉ áp dụng những gì họ biết từ C++ vào nó. Ngược lại, trong Objective-C Apple đã tách bước phân bổ khỏi bước khởi tạo, do đó, các hàm tạo trong ngôn ngữ đó có thể ném ngoại lệ.

C # không thể rò rỉ bộ nhớ từ một cuộc gọi không thành công đến một nhà xây dựng. Thậm chí một số lớp trong .NET framework sẽ đưa ra các ngoại lệ trong các hàm tạo của chúng.

23
Ant

Ném một ngoại lệ IFF, lớp không thể được đưa vào trạng thái nhất quán liên quan đến việc sử dụng ngữ nghĩa của nó. Nếu không thì không. KHÔNG BAO GIỜ cho phép một đối tượng tồn tại ở trạng thái không nhất quán. Điều này bao gồm việc không cung cấp các hàm tạo hoàn chỉnh (như có một hàm tạo rỗng + khởi tạo () trước khi đối tượng của bạn thực sự được xây dựng hoàn chỉnh) ... CHỈ NÓI KHÔNG!

Trong một nhúm, tất cả mọi người làm điều đó. Tôi đã làm điều đó vào một ngày khác cho một đối tượng được sử dụng rất hẹp trong phạm vi chặt chẽ. Một ngày nào đó trên đường, tôi hoặc người khác có thể sẽ trả giá cho sự trượt dốc đó trong thực tế.

Tôi nên lưu ý rằng bởi "constructor", ý tôi là thứ khách hàng gọi để xây dựng đối tượng. Điều đó có thể dễ dàng trở thành một cái gì đó khác hơn là một công trình thực tế có tên là "Nhà xây dựng". Ví dụ: một cái gì đó như thế này trong C++ sẽ không vi phạm nguyên tắc IMNSHO:

struct funky_object
{
  ...
private:
  funky_object();
  bool initialize(std::string);

  friend boost::optional<funky_object> build_funky(std::string);
};
boost::optional<funky_object> build_funky(std::string str)
{
  funky_object fo;
  if (fo.initialize(str)) return fo;
  return boost::optional<funky_object>();
}

Vì cách duy nhất để tạo funky_object bằng cách gọi build_funky nguyên tắc không bao giờ cho phép một đối tượng không hợp lệ tồn tại vẫn còn nguyên vẹn mặc dù "Người xây dựng" thực tế không hoàn thành công việc.

Đó là rất nhiều công việc làm thêm mặc dù cho lợi ích đáng ngờ (thậm chí có thể mất). Tôi vẫn thích con đường ngoại lệ.

13
Edward Strange

Trong trường hợp này, tôi sẽ sử dụng phương pháp nhà máy. Về cơ bản, thiết lập lớp của bạn chỉ có các hàm tạo riêng và có một phương thức xuất xưởng trả về một thể hiện của đối tượng của bạn. Nếu các tham số ban đầu không hợp lệ, chỉ cần trả về null và để mã gọi quyết định phải làm gì.

public class MyClass
{
    private MyClass(string text)
    {
        //normal construction
    }

    public static MyClass MakeMyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
            return null;
        else
            return new MyClass(text);
    }
}
public class CallingClass
{
    public MyClass MakeMyClass(string text)
    {
        var cls = MyClass.MakeMyClass(text);
        if(cls == null)
             //show messagebox or throw exception
        return cls;
    }
}

Không bao giờ ném ngoại lệ trừ khi các điều kiện là đặc biệt. Tôi nghĩ rằng trong trường hợp này, một giá trị trống có thể được thông qua dễ dàng. Nếu đó là trường hợp, sử dụng mẫu này sẽ tránh các trường hợp ngoại lệ và các hình phạt về hiệu suất mà chúng phải chịu trong khi vẫn giữ trạng thái MyClass hợp lệ.

9
Donn Relacion
  • Một nhà xây dựng không nên có bất kỳ tác dụng phụ nào. [.__.]
    • Bất cứ điều gì nhiều hơn khởi tạo trường riêng nên được xem là một tác dụng phụ.
    • Một nhà xây dựng với các tác dụng phụ phá vỡ Nguyên tắc Đơn trách nhiệm (SRP) và chạy ngược lại với tinh thần của lập trình hướng đối tượng (OOP).
  • Một nhà xây dựng nên nhẹ nhàng và không bao giờ thất bại. [.__.]
    • Chẳng hạn, tôi luôn rùng mình khi thấy một khối thử bắt bên trong một hàm tạo. Một constructor không nên ném ngoại lệ hoặc lỗi log.

Người ta có thể đặt câu hỏi một cách hợp lý các hướng dẫn này và nói, "Nhưng tôi không tuân theo các quy tắc này và mã của tôi hoạt động tốt!" Về điều đó, tôi sẽ trả lời, "Vâng, điều đó có thể đúng, cho đến khi nó không."

  • Các ngoại lệ và lỗi bên trong của một constructor là rất bất ngờ. Trừ khi họ được yêu cầu làm như vậy, các lập trình viên trong tương lai sẽ không có xu hướng bao quanh các cuộc gọi của nhà xây dựng này với mã phòng thủ.
  • Nếu bất cứ điều gì thất bại trong sản xuất, dấu vết ngăn xếp được tạo ra có thể khó phân tích. Đỉnh của dấu vết ngăn xếp có thể trỏ đến lệnh gọi của hàm tạo, nhưng nhiều điều xảy ra trong hàm tạo và nó có thể không trỏ đến LỘC thực tế đã thất bại. [.__.]
    • Tôi đã phân tích nhiều dấu vết ngăn xếp .NET trong trường hợp này.
2
Jim G.

Sở thích của tôi là đặt mặc định, nhưng tôi biết Java có thư viện "Apache Commons" hoạt động như thế này và tôi nghĩ đó cũng là một ý tưởng khá hay. Tôi không thấy một vấn đề với việc ném ngoại lệ nếu giá trị không hợp lệ sẽ đặt đối tượng ở trạng thái không sử dụng được, một chuỗi không phải là một ví dụ hay nhưng nếu đó là đối với DI của người nghèo thì bạn không thể hoạt động nếu giá trị null đã được thông qua thay thế, giả sử, giao diện ICustomerRepository. Trong tình huống như thế này, ném ngoại lệ là cách xử lý chính xác, tôi sẽ nói.

0
Wayne Molina

Nó phụ thuộc vào những gì MyClass đang làm. Nếu MyClass thực sự là một lớp kho lưu trữ dữ liệu và văn bản tham số là một chuỗi kết nối, thì cách tốt nhất là ném một ArgumentException. Tuy nhiên, nếu MyClass là lớp StringBuilder (chẳng hạn), thì bạn có thể để trống.

Vì vậy, nó phụ thuộc vào cách văn bản tham số thiết yếu đối với phương thức - đối tượng có ý nghĩa với giá trị null hoặc trống không?

0
Steven Striga