2010-02-01 67 views
6

Tôi có một số lớp phản ánh các bảng trong cơ sở dữ liệu. Tôi muốn có một lớp cơ sở có một số chức năng cơ bản (nói, nó sẽ có một lá cờ "isDirty"), và một mảng tĩnh của các chuỗi với các tên cột khi chúng xuất hiện trong cơ sở dữ liệu. Các mã sau đây không làm việc nhưng minh họa những gì tôi muốn làm:Thừa kế thành viên tĩnh trong C#

public class BaseRecord { 
    public bool isDirty; 
    public object [] itemArray; 
    public static string [] columnNames;  
} 

public class PeopleRec : BaseRecord { 
} 

public class OrderRec : BaseRecord { 
} 

public static void Main() { 
    PeopleRec.columnNames = new string[2]; 
    PeopleRec.columnNames[0]="FIRST_NAME"; 
    PeopleRec.columnNames[1]="LAST_NAME"; 

    OrderRec.columnNames = new string[4]; 
    OrderRec.columnNames[0] = "ORDER_ID"; 
    OrderRec.columnNames[1] = "LINE"; 
    OrderRec.columnNames[2] = "PART_NO"; 
    OrderRec.columnNames[3] = "QTY"; 
} 

public class DoWork<T> where T : BaseRecord { 
    public void DisplayColumnNames() { 
    foreach(string s in T.columnNames) 
     Console.Write("{0}", s); 
    } 
    public void DisplayItem(T t) { 
    for (int i=0; i<itemValues.Length; i++) { 
     Console.Write("{0}: {1}",t.columnNames[i],t.itemValues[i]) 
    } 
    } 
} 

Tôi muốn mỗi lớp có nguồn gốc để có nó là mảng tĩnh riêng của chuỗi các tên cột cơ sở dữ liệu, và tôi muốn các lớp tổng quát để truy cập thành viên tĩnh này mà không cần một cá thể.

Nhưng nó không hoạt động:
(A) cộtNames là mảng giống nhau trong BaseRec, PeopleRec và OrderRec. Tôi không thể có columnNames khác nhau. BaseRec.columnNames.Length sẽ là 3 vì columnNames trong OrderRec được khởi tạo lần cuối.
(B) Ký hiệu T.columnNames không biên dịch.

Bất kỳ ý tưởng nào về cách sửa lỗi này?

Trả lời

3

Vấn đề là bạn mà bạn muốn kết hợp một số dữ liệu với các loại, không phải với trường hợp của các loại . Tôi không chắc chắn rằng có một cách gọn gàng để làm điều này trong C#, nhưng một khả năng là sử dụng một static Dictionary<Type, string[]> trên BaseRecord. Một ví dụ là dưới đây, bạn có thể neaten này lên bằng cách thêm một số thành viên tĩnh chung trên BaseRecord cho khởi tạo/truy cập vào tên kỷ lục (và thêm một số kiểm tra lỗi ...):

using System; 
using System.Collections.Generic; 

namespace Records 
{ 
    public class BaseRecord 
    { 
     public bool isDirty; 
     public object[] itemArray; 

     public static Dictionary<Type, string[]> columnNames = new Dictionary<Type, string[]>(); 
    } 

    public class PeopleRec : BaseRecord 
    { 
     static PeopleRec() 
     { 
      string[] names = new string[2]; 
      names[0] = "FIRST_NAME"; 
      names[1] = "LAST_NAME"; 
      BaseRecord.columnNames[typeof(PeopleRec)] = names; 
     } 
    } 

    public class DoWork<T> where T : BaseRecord 
    { 
     public void DisplayColumnNames() 
     { 
      foreach (string s in BaseRecord.columnNames[typeof(T)]) 
       Console.WriteLine("{0}", s); 
     } 

     public void DisplayItem(T t) 
     { 
      for (int i = 0; i < t.itemArray.Length; i++) 
      { 
       Console.WriteLine("{0}: {1}", BaseRecord.columnNames[typeof(T)][i], t.itemArray[i]); 
      } 
     } 
    } 

    class Program 
    { 
     public static void Main() 
     { 
      PeopleRec p = new PeopleRec 
      { 
       itemArray = new object[] { "Joe", "Random" } 
      }; 

      DoWork<PeopleRec> w = new DoWork<PeopleRec>(); 
      w.DisplayColumnNames(); 
      w.DisplayItem(p); 
     } 
    } 
} 
+0

Cảm ơn Dave. Tôi xem xét việc sử dụng một từ điển như là một phương sách cuối cùng (chỉ đơn giản là do các chi phí hiệu suất trên không), nhưng tôi đã không nghĩ đến việc sử dụng typeof (T) làm trường khóa. Giải pháp của bạn chắc chắn là thanh lịch. –

+0

Cảm ơn rất nhiều. Giải pháp của tôi sử dụng các thuộc tính trên các lớp, nhưng nó rất chậm nếu bạn cần phải nhấn nó nhiều lần (vì sự phản chiếu) ... Tôi có thể chuyển sang điều này nếu tôi cần đến điều này rất nhiều. – Crisfole

1

Tại sao bạn không chỉ tạo các lớp riêng biệt của "Tên toàn cầu", các thành viên chỉ là các chuỗi cố định tĩnh chứa văn bản bạn cần truy cập.

public static class OrderRecNames{ 
    public static const string OrderId = "ORDER_ID"; 
    public static const string Line = "LINE"; 
    public static const string PartNo = "PART_NO"; 
    public static const string Qty = "QTY"; 
} 

Edit:

Hơn thế nữa, làm cho mỗi lớp tĩnh của toàn cầu Tên lớp bên trong của lớp họ mô tả, vì vậy thay vì OrderRecNames.OrderId, bạn có thể gõ OrderRec.Names.OrderId, vì vậy mục đích của lớp học là rất rõ ràng. Hy vọng rằng sẽ giúp!

Chỉnh sửa 2: (rất cụ thể)

public class BaseRecord { 
    public bool isDirty; 
    public object [] itemArray; 
} 

public class PeopleRec : BaseRecord { 
    class Columns { 
     public static const string FirstName = "FIRST_NAME"; 
     public static const string LastName = "LAST_NAME"; 
    } 
} 

public class OrderRec : BaseRecord { 
    class Columns { 
     public static const string OrderId = "ORDER_ID"; 
     public static const string Line = "LINE"; 
     public static const string PartNo = "PART_NO"; 
     public static const string Qty = "QTY"; 
    } 
} 
+0

Bởi vì tôi muốn một lớp chung để làm việc trên bất kỳ số lượng trẻ em của BaseRecord, tôi cần phải có một mảng hoặc danh sách hoặc một cái gì đó của các lĩnh vực, và không được đặt tên trường (như trong, tôi không thể có các lĩnh vực được gọi là FirstName, LastName , v.v.) Ngoài ra, tôi có thể truy cập lớp "Cột" như T.Columns.OrderID trong lớp chung không? –

1

Mảng là kiểu tham chiếu. Chỉ cần bao gồm một thuộc tính không tĩnh trong cơ sở, và thực hiện nó trong các lớp dẫn xuất của bạn bằng cách tạo một thành viên tĩnh cho tất cả các cá thể tham chiếu đến. Chi phí thực sự khá nhỏ. Tôi hứa bạn sẽ không nhận thấy.

Ồ, và ReadOnlyCollection<string> sẽ tốt hơn một mảng cho những tên đó. Một cái gì đó như thế này:

public class BaseRecord { 
    public bool isDirty; 
    public IEnumerable<Object> items; 
    public ReadOnlyCollection<string> columnNames;  
} 

hoặc

public class BaseRecord { 
    public bool isDirty; 
    public IList<Object> items; 

    public Object this[int index] 
    { 
    get { return items[index];} 
    set { items[index] = value;} 
    } 

    public ReadOnlyCollection<string> columnNames;  
} 

hoặc với C# 4 (assuming my understanding of dynamic is correct):

public class BaseRecord { 
    public bool isDirty; 
    public IList<dynamic> items; 

    public dynamic this[int index] 
    { 
    get { return items[index];} 
    set { items[index] = value;} 
    } 

    public ReadOnlyCollection<string> columnNames;  
} 

Thực sự, tuy nhiên, tôi đặt câu hỏi về lợi ích của lớp này. Giao diện nó cung cấp chỉ cung cấp cho bạn truy cập không gõ vào dữ liệu thực tế, và điều đó không phải là rất hữu ích. Bạn có thể cố gắng sửa chữa nó bằng generics, nhưng sau đó bạn kết thúc với một cái gì đó như thế này:

public Record<T> 
{ 
    public bool isDirty; 
    public ReadOnlyCollection<string> columnNames; 
    public T data; 
} 

trong đó mỗi "T" là một lớp hoàn toàn khác với các trường từ một bảng. Nó không cung cấp cho bạn cú pháp tốt, và vào thời điểm bạn thực hiện các lớp khác, bạn có thể chỉ cần tack các tên cột và thành viên isDirty trong trực tiếp.

Dường như với tôi rằng giải pháp tốt hơn là khi tôi đề cập đến một "giao diện" trước đó.Những gì bạn thực sự muốn trông giống như thế này:

public IRecord 
{ 
    bool isDirty; 
    ReadOnlyCollection<string> columnNames; 
} 

hoặc lẽ:

public IRecord 
{ 
    bool isDirty; 
    ReadOnlyCollection<string> columnNames; 

    dynamic this[int index] 
    { 
     get; 
     set; 
    } 
} 
+0

Tôi đánh giá cao câu trả lời của bạn. Tuy nhiên, tôi lo ngại về việc tăng gấp đôi dung lượng cần thiết. Nếu mỗi IRecord hoặc BaseRecord hoặc bất cứ thứ gì có cả dữ liệu và mảng columnNames thì nó sẽ tăng gấp đôi (hoặc nhiều hơn) kích thước. Tôi đã đạt đến giới hạn bộ nhớ cho ứng dụng của mình. –

+0

@Marc - Bạn đã bỏ lỡ một điểm quan trọng: Tôi không ** đề xuất bạn giữ một bản sao của mảng cho mỗi IRecord! Mảng là loại tham chiếu. Tôi đề nghị bạn xây dựng một bộ sưu tập tĩnh riêng biệt cho các loại IRecord của bạn và thực hiện thuộc tính columnNames của bạn với tham chiếu đến bộ sưu tập tĩnh đó. Đó là chỉ 16 byte cho mỗi trường hợp. Tôi hứa ứng dụng của bạn có thể xử lý điều đó. –

+0

Joel: Tôi chắc chắn đã bỏ lỡ vấn đề. Bạn đang nói tạo chuỗi các tên cột và lưu trữ nó ở đâu đó, và có mỗi cá thể của điểm IRecord (tham chiếu) mảng đó. Tôi đã bỏ lỡ điều đó khi lần đầu tiên tôi đọc nó. Thật vậy, 16 byte cho mỗi trường hợp sẽ không thể bỏ qua. Cảm ơn! –

-1

Nếu bạn đã phải có tĩnh biến tôi sẽ làm điều này:

public class BaseRecord 
    { 
    public bool isDirty; 
    public object[] itemArray; 
    public static string[] columnNames; 

    public void DisplayColumnNames() 
    { 
     foreach (string s in columnNames) 
      Console.Write("{0}\n", s); 
    } 

    public void DisplayItem() 
    { 
     for (int i = 0; i < itemArray.Length; i++) 
     { 
      Console.Write("{0}: {1}\n", columnNames[i], itemArray[i]); 
     } 
    } 
    } 

    public class PeopleRec : BaseRecord 
    { 
    public PeopleRec() 
    { 
    } 
    } 

    public class OrderRec : BaseRecord 
    { 
    public OrderRec() 
    { 
    } 
    } 

    public static void Main() 
    { 
    PeopleRec.columnNames = new string[2]; 
    PeopleRec.columnNames[0] = "FIRST_NAME"; 
    PeopleRec.columnNames[1] = "LAST_NAME"; 

    OrderRec.columnNames = new string[4]; 
    OrderRec.columnNames[0] = "ORDER_ID"; 
    OrderRec.columnNames[1] = "LINE"; 
    OrderRec.columnNames[2] = "PART_NO"; 
    OrderRec.columnNames[3] = "QTY"; 

    BaseRecord p = new PeopleRec(); 
    p.DisplayColumnNames(); 
    p.itemArray = new object[2]; 
    p.itemArray[0] = "James"; 
    p.itemArray[1] = "Smith"; 
    p.DisplayItem(); 

    BaseRecord o = new OrderRec(); 
    o.DisplayColumnNames(); 
    o.itemArray = new object[4]; 
    o.itemArray[0] = 1234; 
    o.itemArray[1] = 1; 
    o.itemArray[2] = 39874; 
    o.itemArray[3] = 70; 
    o.DisplayItem(); 
    } 

Nếu không, tôi sẽ thay đổi chúng để tài sản công cộng (không tĩnh) và chỉ cần thêm chúng vào mảng lớp có nguồn gốc tạo ra (loại bỏ các cuộc gọi tĩnh với một cuộc gọi bất động sản).

+0

Điều này sẽ không hoạt động chút nào - mảng tĩnh của các tên cột sống trong lớp cơ sở và sẽ bị ghi đè khi bất kỳ lớp con nào được khởi tạo (tạo một PeopleRec, sau đó là một OrderRec và kiểm tra từng trường hợp được tạo) tính chất). Ngoài ra, bạn có lẽ không nên có logic đó để thiết lập các tên này trong hàm tạo, vì 99,9% thời gian, các tên cột không thay đổi trong khi thực thi. – Pwninstein

Các vấn đề liên quan