2012-04-27 26 views
14

Tình hìnhAlternatives để typedef hoặc chuỗi subclassing trong C#

tôi có lớp học mà những giao dịch trong nội bộ với nhiều loại khác nhau của đường dẫn tập tin: một số địa phương, một số từ xa; một số người thân, một số tuyệt đối.

Nó từng là trường hợp nhiều phương pháp của nó truyền chúng xung quanh với nhau là string s, nhưng rất khó để theo dõi chính xác loại đường dẫn mà mỗi phương thức mong đợi.

Việc sửa chữa mong muốn

Vì vậy, chúng tôi chủ yếu muốn typedef bốn loại khác nhau để string: RemoteRelative, LocalRelative, RemoteAbsolute, và LocalAbsolute. Bằng cách này, trình kiểm tra loại tĩnh có thể giúp nhà phát triển đảm bảo rằng họ đang cung cấp và mong đợi string s với ngữ nghĩa chính xác.

Thật không may, stringsealed trong BCL, vì vậy chúng tôi không thể thực hiện việc này với kế thừa đơn giản. Và không có đơn giản typedef, vì vậy chúng tôi cũng không thể làm theo cách đó.

Việc sửa chữa thực tế

tôi đã kết thúc việc tạo ra bốn lớp đơn giản khác nhau mà mỗi chứa một readonly string.

public struct LocalAbsolutePath { 
    public readonly string path; 
    public LocalAbsolutePath(string path) { 
     this.path = path; 
    } 
} 

Điều đó chủ yếu là hoạt động, nhưng nó kết thúc bằng cách thêm một chút độ dài không mong muốn.

Câu hỏi: Tôi có thấy bất kỳ lựa chọn thay thế nào phù hợp tự nhiên với cú pháp C# đơn giản không?

Như tôi đã đề cập ở trên, kiểu C typedef string LocalAbsolutePath; hoặc thậm chí kiểu F # type LocalAbsolutePath = string sẽ là ước mơ của tôi ở đây. Nhưng ngay cả một cái gì đó là một bước mà hướng từ các lớp học tùy chỉnh sẽ là tuyệt vời.

+0

không là nó có thể tìm thấy một mô hình phổ biến phù hợp với cách? Ví dụ: nếu đường dẫn tuyệt đối, phải có một số ký tự ổ đĩa trước, nếu không, nó * phải * là tương đối, tương tự cho đường dẫn mạng ... – Tigran

+0

@Tigran, hoàn toàn có, nhưng đó là vấn đề trong lần đầu tiên địa điểm. Tôi muốn _compiler_ có thể biết được liệu chúng ta có truyền đi đúng loại không, không phải là thời gian chạy. – sblom

+2

Về cơ bản lấy mã của tôi và @ dasblinkenlight cùng nhau và bạn có cho mình một lớp học. :) –

Trả lời

0

Tôi đã tạo gói NuGet có tên LikeType cung cấp hành vi giống như typedef trong các lớp C#.

Đây là cách bạn sẽ sử dụng nó:

class CustomerId : LikeType<string> 
{ 
    public CustomerId(string id) : base(id) { } 
} 

Sau đây là cách loại sẽ cư xử:

void ShowTypeBehavior() 
{ 
    var customerId = new CustomerId("cust-001"); // create instance with given backing value 
    string custIdValue = customerId; // implicit cast from class to backing type, sets 'custIdValue' to "cust-001" 

    var otherCustomerId = new CustomerId("cust-002"); 
    var areEqual = customerId == otherCustomerId; // false 
    var areNotEqual = customerId != otherCustomerId; // true 
    var areEqualUsingMethod = customerId.Equals(otherCustomerId); // false 

    var customerIdCopy = new CustomerId("cust-001"); // create separate instance with same backing value 
    var isCopyEqual = customerId == customerIdCopy; // true. Instances are considered equal if their backing values are equal. 
} 
14

Giải pháp của bạn tốt. Bạn có thể chống lại mức độ chi tiết bổ sung bằng cách thêm chuyển đổi loại thành string, cho phép bạn sử dụng LocalAbsolutePath ở bất kỳ đâu có thể string.

public struct LocalAbsolutePath { // Making it a class would be OK too 
    private readonly string path; // <<=== It is now private 
    public LocalAbsolutePath(string path) { 
     this.path = path; 
    } 
    public static implicit operator string(LocalAbsolutePath p) { 
     return p.path; 
    } 
} 
5

Những gì bạn cần làm là giữ cách tiếp cận hiện tại của bạn tạo ra bốn lớp riêng biệt cho các loại đường dẫn của bạn (và thậm chí có họ kế thừa baseclass giống nhau), do đó bạn có thể hạn chế phương pháp để chỉ nhận được một trong bốn Path các đối tượng.

Mặc dù tôi không cảm thấy rằng var myPath = new LocalAbsolutePath("path") thực sự là chi tiết hơn var myPath = "path", vì nó thiếu ngắn gọn, nhưng nếu bạn thực sự muốn, bạn có thể triển khai toán tử đúc ẩn giữa lớp của bạn và chuỗi, và có công việc này:

public static implicit operator LocalAbsolutePath(string path) 
{ 
    return new LocalAbsolutePath(path); 
} 

và bây giờ bạn chỉ có thể làm:

LocalAbsolutePath myPath = "Path String"; 
-1

có lẽ tôi đang sủa lên cây sai ở đây, nhưng typedef được thực hiện như kiểu bí danh trong C#, theo như tôi biết. Thiết lập bí danh loại đơn giản như sau:

using LocalAbsolutePath = System.String; 

Sau đó, bạn có thể bắt đầu sử dụng LocalAbsolutePath làm loại hợp lệ. Nhiều hơn hoặc ít hơn như thế này:

LocalAbsolutePath thisPath = "c:\\thisPath"; 

Dựa trên bài đăng của bạn, tôi cảm thấy đây là những gì bạn đang tìm kiếm. Hy vọng tôi đúng ...!

+0

Một báo trước: bạn không thể bí danh bí danh. Do đó câu lệnh 'using LocalAbsolutePath = string' sẽ không hoạt động vì' string' là bí danh kiểu chung cho 'System.String'. Ngoài ra, bạn nên tốt. – code4life

+0

Tôi có thể làm điều đó 4 lần với 4 tên khác nhau không? Và trình biên dịch sẽ xem xét từng tên trong số 4 loại khác biệt này? – sblom

+0

Có, bạn có thể, trình biên dịch sẽ nhận ra nó. – code4life

0

Vì tôi đã đặt ra mục tiêu tương tự trong một dự án tôi đang làm việc và tôi được hưởng lợi rất nhiều từ các câu trả lời ở đây, tôi nghĩ tôi sẽ chia sẻ giải pháp mà tôi đã kết thúc. Đối phó với null, đặc biệt là trong các thử nghiệm kiểm tra đơn vị, gần như đã khiến tôi phát điên. Tất nhiên sau đây sẽ thất bại:

string someStringVar = null; 
MyStringType myStringType = new MyStringType(someStringVar); 
MyStringType myStringTypeNull = null; 
Assert.AreEqual(myStringType, myStringTypeNull); 

Sử dụng phân tích tĩnh() thay cho hàm tạo công khai thỏa mãn hơn vì nó cho phép tôi trả về giá trị rỗng. Điều này vượt qua:

string someStringVar = null; 
MyStringType myStringType = MyStringType.Parse(someStringVar); 
MyStringType myStringTypeNull = null; 
Assert.AreEqual(myStringType, myStringTypeNull); 

Ngoài ra, tôi không muốn chuyển đổi một cách rõ ràng từ chuỗi thành MyStringType - điều này dường như làm cho một số lợi ích có ý thức mã hóa thực hiện điều này ngay từ đầu. Việc cho phép chuyển đổi ngầm từ chuỗi có nghĩa là một cuộc gọi đến một phương thức có tham số MyStringType sẽ chấp nhận một chuỗi và tôi không muốn điều này vì một phương thức có nhiều tham số chuỗi rất dễ bị lỗi. Việc chuyển đổi ngược lại hoàn toàn có ý nghĩa hơn với tôi.

Cuối cùng, tôi nghĩ đây là một trường hợp lý tưởng cho một loại thuốc generic có thể dễ dàng tái sử dụng.

Dù sao, đây là những gì tôi đã kết thúc với:

public class StringType<T> where T:class 
{ 
    private readonly string _str; 

    protected StringType(string str) 
    { 
     _str = str; 
    } 

    public static implicit operator string(StringType<T> obj) 
    { 
     return obj == null ? null : obj._str; 
    } 

    public override string ToString() 
    { 
     return _str; 
    } 

    public override int GetHashCode() 
    { 
     return _str.GetHashCode(); 
    } 
} 


public class MyStringType : StringType<MyStringType> 
{ 
    protected MyStringType(string str) : base(str) { } 

    public static MyStringType Parse(object obj) 
    { 
     var str = obj is string ? (string)obj : (obj == null ? null : obj.ToString()); 
     return str == null ? null : new MyStringType(str); 
    } 
} 

Comments/cải tiến/đơn giản hóa tất nhiên hoan nghênh!