2011-09-12 21 views
12

Boxing chuyển đổi loại giá trị thành loại đối tượng. Hoặc như MSDN đặt nó, boxing là một "hoạt động để bọc cấu trúc bên trong một đối tượng kiểu tham chiếu trên heap được quản lý."C# - Có thể sử dụng các hộp bể bơi không?

Nhưng nếu bạn cố gắng tìm hiểu điều đó bằng cách xem mã IL, bạn chỉ thấy từ "hộp" ma thuật.

suy đoán, tôi đoán rằng bộ thực thi có một số loại thuốc generic dựa trên lớp bí mật lên tay áo của mình, giống như Box<T> với một tài sản public T Value, và đấm bốc một int sẽ trông như thế:

int i = 5; 
Box<int> box = new Box<int>; 
box.Value = 5; 

Unboxing int sẽ rẻ hơn rất nhiều: return box.Value;

Thật không may, ứng dụng máy chủ hoạt động kém hiệu quả của tôi có một chút công bằng, đặc biệt là số thập phân. Tệ hơn nữa, những chiếc hộp này có thời gian tồn tại ngắn, điều này khiến tôi nghi ngờ tôi phải trả hai lần, một lần cho việc phát hành hộp và sau đó một lần nữa để thu gom rác sau khi tôi hoàn thành nó.

Nếu tôi đã tự mình khai thác bộ nhớ này, tôi sẽ xem xét việc sử dụng một nhóm đối tượng ở đây. Nhưng kể từ khi tạo vật thể thực sự được ẩn đằng sau một từ kỳ diệu trong IL, lựa chọn của tôi là gì?

câu hỏi cụ thể của tôi:

  • Có một cơ chế hiện có để gây runtime để có hộp từ một hồ bơi chứ không phải instanciating họ?
  • Loại cá thể được tạo ra trong thời gian boxing là gì? Có thể tự kiểm soát quá trình boxing, nhưng vẫn tương thích với unboxing không?

Nếu câu hỏi cuối cùng có vẻ lạ, ý tôi là tôi có thể tự tạo lớp Box<T> hoặc DecimalBox của riêng mình, xếp chồng và hộp/hộp thủ công. Nhưng tôi không muốn phải đi và sửa đổi những nơi khác nhau trong mã mà tiêu thụ giá trị đóng hộp (aka unbox nó).

+0

Bạn đã cân nhắc việc giảm hoạt động quyền anh bằng cách sử dụng generics/custom objects/etc.? Lệnh 'box' là cố ý mờ đục, mặc dù bạn có thể bắt chước nó thông qua lớp' Box 'ở trên. – dlev

+10

"ứng dụng của tôi có một chút công bằng boxing". Một trò chơi kỳ lạ, động thái chiến thắng duy nhất không phải là để chơi. –

+5

"Tồi tệ hơn, những chiếc hộp này tồn tại trong thời gian ngắn, khiến tôi nghi ngờ tôi phải trả hai lần, một lần cho việc phát hành hộp và sau đó một lần nữa để thu gom rác sau khi tôi hoàn thành nó." Bạn đã lược tả điều đó, đúng không? tức là bạn biết rằng GC là một nút cổ chai trong đơn đăng ký của bạn vì bạn đã đo lường và không phải vì bạn đã đoán. Thời gian duy nhất tôi quản lý để có được GC ràng buộc đã được phân bổ mảng byte lớn thường xuyên. Nếu bộ sưu tập Gen0 là một nút cổ chai, bạn nên xem xét lại thiết kế của mình. – CodesInChaos

Trả lời

10

suy đoán, tôi đoán rằng bộ thực thi có một số loại thuốc generic dựa trên lớp bí mật lên tay áo của mình

suy đoán của bạn là gần như ngay. Một cách hợp lý bạn có thể nghĩ về một hộp như là một loại ma thuật Box<T> ma thuật hoạt động như bạn mô tả (với một vài bit ma thuật; ví dụ, cách mà hộp giá trị rỗng có một chút khác thường.) , thời gian chạy không làm điều đó với các loại chung chung. Quyền anh tồn tại trong CLR v1, trước khi các loại chung được thêm vào hệ thống kiểu.

ứng dụng máy chủ hoạt động kém hiệu quả của tôi thực hiện một chút công bằng, đặc biệt là số thập phân.

Nếu bị đau khi bạn làm như vậy thì ngừng làm điều đó. Thay vì cố gắng để làm cho boxing rẻ hơn, ngừng làm nó ở nơi đầu tiên. Tại sao bạn lại là một số thập phân?

Tệ hơn nữa, những hộp này tồn tại trong thời gian ngắn, khiến tôi nghi ngờ tôi phải trả hai lần, một lần để chỉnh sửa hộp và sau đó lại thu gom rác sau khi hoàn thành.

Thời gian sống ngắn là tốt hơn dài hơn thời gian sống; với các đối tượng heap ngắn ngủi bạn trả tiền để thu thập chúng một lần và sau đó chúng đã chết. Với đối tượng heap tồn tại lâu dài, bạn phải trả chi phí đó nhiều lần khi đối tượng tiếp tục tồn tại.

Tất nhiên, chi phí mà bạn có thể lo lắng về các đối tượng sống ngắn không phải là chi phí cho mỗi lần thu. Đúng hơn, đó là áp lực thu ; nhiều đối tượng sống ngắn hơn được phân bổ bằng bộ sưu tập rác thường xuyên hơn.

Chi phí phân bổ khá nhỏ. Di chuyển con trỏ trên đống GC, sao chép số thập phân vào vị trí đó, thực hiện.

Nếu tôi tự mình tự mình bộ nhớ này, tôi sẽ xem xét việc sử dụng nhóm đối tượng tại đây.

Phải; bạn trả chi phí thu thập đối tượng tồn tại lâu hơn, nhưng bạn có ít bộ sưu tập hơn vì áp lực thu thập ít hơn được tạo ra. Đó có thể là một chiến thắng.

Có cơ chế hiện tại để thực hiện thời gian chạy để lấy hộp từ một hồ bơi thay vì instanciating chúng?

Không.

Loại cá thể được tạo ra trong thời gian boxing là gì? Có thể tự kiểm soát quá trình boxing, nhưng vẫn tương thích với unboxing không?

Loại hộp là loại thứ được đóng hộp. Chỉ cần hỏi nó bằng cách gọi GetType; nó sẽ cho bạn biết. Hộp là huyền diệu; chúng là loại vật chứa chúng.

Như tôi đã nói trước đây, thay vì cố gắng làm cho boxing rẻ hơn, chỉ cần không làm điều đó ngay từ đầu.

+0

Có những tình huống mà boxing rất khó tránh. Một trong những tình huống đó là khi bạn đang xây dựng một khung thực thể có đăng ký thuộc tính tĩnh và quyền truy cập kiểu GetValue/SetValue, nghĩa là giống như các đối tượng/thuộc tính phụ thuộc WPF. Cuối cùng, chúng ta có thể sẽ sử dụng phép thuật voodoo 'Reflection.Emit' để tránh đấm bốc nhưng đó là một công việc lớn vào thời điểm này ... xem câu trả lời dưới đây cho bộ nhớ đệm mà chúng ta đã giải quyết vấn đề của chúng ta trong thời gian chờ đợi :) –

2

Không có lớp học nào theo mặc định là Box<T>. Kiểu này vẫn là kiểu gốc, nhưng là một tham chiếu. Kể từ Decimal không thay đổi, bạn không thể thay đổi giá trị sau khi tạo. Vì vậy, bạn không thể thực sự sử dụng tổng hợp với các số thập phân đóng hộp bình thường.

Bạn có thể tránh boxing cho các giá trị tái xuất hiện bằng cách triển khai bộ nhớ cache. Hoặc bạn cần phải thực hiện loại hộp của riêng bạn.

Không thể bỏ hộp kiểu hộp của riêng bạn từ object với dàn diễn viên chuẩn. Vì vậy, bạn sẽ cần phải thích ứng với mã tiêu thụ.


Viết phương pháp của riêng bạn mà trả về một giá trị đóng hộp:

[ThreadStatic] 
private static Dictionary<T,object> cache; 

public object BoxIt<T>(T value) 
    where T:struct 
{ 
    if(cache==null) 
    cache=new Dictionary<T,object>(); 
    object result; 
    if(!cache.TryGetValue(T,result)) 
    { 
    result=(object)T; 
    cache[value]=result; 
    } 
    return result; 
} 

Một vấn đề với việc thực hiện đơn giản này là bộ nhớ cache không bao giờ loại bỏ mặt hàng này. I E. nó là một rò rỉ bộ nhớ.

+0

Bộ nhớ cache này có bất kỳ trợ giúp nào? Nó vẫn tạo ra các đối tượng tương tự, chỉ không bao giờ deallocates chúng. – configurator

+0

Nó không tạo đối tượng nếu bạn sử dụng cùng một giá trị thường xuyên. Nhưng nếu hầu hết các giá trị là duy nhất nó sẽ là xấu. Vì tôi không biết OP đang làm gì, tôi không biết liệu bộ nhớ cache có hữu ích hay không. Java sử dụng một mẫu tương tự bên trong khi các số nguyên nhỏ boxing (-128 đến 127 nếu tôi nhớ chính xác). – CodesInChaos

0
int i = 5; 
object boxedInt = i; 

Gán một loại giá trị cho một System.Object là nhiều hơn hoặc ít hơn tất cả những gì boxing như xa như mã của bạn là có liên quan (Tôi sẽ không đi vào chi tiết kỹ thuật hoạt động đấm bốc).

Giữ giá trị thập phân của bạn trong System.Object biến có thể tiết kiệm một chút thời gian từ quyền anh và tạo ra các trường hợp System.Object nhưng bạn luôn phải bỏ hộp. Điều này trở nên khó thực hiện hơn nếu bạn phải thay đổi những giá trị đó thường xuyên vì mỗi thay đổi là một nhiệm vụ, và do đó ít nhất là quyền anh.

Có một ví dụ về thực hành này mặc dù - .Net framework sử dụng các giá trị boolean tiền đóng hộp trong nội bộ trong một lớp học tương tự như sau:

class BoolBox 
{ 
    // boxing here 
    private static object _true = true; 
    // boxing here 
    private static object _false = false; 

    public static object True { get { return _true; } } 
    public static object False { get { return _false; } } 
} 

Hệ thống WPF thường sử dụng System.Object biến cho thuộc tính phụ thuộc, chỉ để đặt tên cho một trường hợp mà boxing/unboxing là không thể tránh khỏi ngay cả trong những 'thời hiện đại' này.

4

Thời gian chạy thực hiện khá nhiều điều bạn mô tả, tuy nhiên Generics không liên quan, vì Generics không phải là một phần của khung ban đầu.

Không có nhiều điều bạn có thể làm về đấm bốc, nếu bạn đang sử dụng một số mã dự kiến ​​có giá trị được đóng hộp. Bạn có thể tạo một đối tượng sử dụng cùng một cú pháp để trả về một giá trị bằng cách ghi đè chuyển đổi ngầm, nhưng điều đó vẫn cần phải là một đối tượng, và về cơ bản bạn sẽ làm cùng một lượng công việc.

Việc thử các giá trị đóng hộp trong hồ bơi sẽ rất có thể làm giảm hiệu suất hơn là tăng hiệu suất. Bộ thu gom rác được chế tạo đặc biệt để xử lý các vật thể sống ngắn một cách hiệu quả, và nếu bạn đặt các vật thể trong một hồ bơi, chúng sẽ là các vật thể sống lâu. Khi các đối tượng tồn tại trong một bộ sưu tập rác, chúng sẽ được chuyển đến heap tiếp theo, bao gồm việc sao chép đối tượng từ một nơi này sang bộ nhớ khác. Vì vậy, bằng cách gộp các đối tượng bạn thực sự có thể gây ra nhiều công việc hơn cho bộ thu gom rác thay vì ít hơn.

+0

Câu trả lời của bạn khá đúng, nhưng chiến lược gộp lại có ý nghĩa trong nhiều trường hợp. Có, bạn kết thúc với các đối tượng tồn tại lâu hơn, đắt tiền vì chúng tồn tại nhiều bộ sưu tập. Nhưng nếu mọi thứ được gộp lại, thì hầu như không có bất kỳ bộ sưu tập nào bởi vì không có gì tạo áp lực thu thập. Ý tưởng tổng hợp không phải là giảm chi phí cho mỗi đối tượng của bộ sưu tập; thay vào đó, ý tưởng là giảm tổng số bộ sưu tập. –

+0

Có, việc gộp nhóm có thể giúp ích trong một số trường hợp, nhưng gộp chung Các giá trị thập phân không có khả năng mang lại nhiều lần truy cập cho các giá trị hiện có. Nếu cùng một giá trị sẽ được sử dụng hết lần này đến lần khác, việc lưu vào bộ nhớ đệm kết quả sẽ hiệu quả hơn nhiều so với việc lưu vào bộ nhớ đệm các thông số đầu vào. – Guffa

+0

Tôi đồng ý rằng tổng hợp * theo giá trị * có lẽ không phải là một ý tưởng hay. Nhưng bạn có thể hồ bơi chỉ là hộp; khi hộp không còn được sử dụng nữa, nó sẽ trở lại hồ bơi và tự dọn sạch. Chúng tôi sử dụng kỹ thuật này trong nhóm biên dịch để giảm bớt áp lực thu thập.Phía xuống là bạn mất rất nhiều lợi ích của bộ nhớ được thu thập tự động; nó chỉ hoạt động nếu hầu hết thời gian bạn nói rõ ràng khi bạn hoàn thành một hộp để nó có thể trở lại hồ bơi. –

0

Thật không may là bạn không thể móc quá trình boxing, tuy nhiên, bạn có thể sử dụng chuyển đổi ngầm để lợi thế của bạn để làm cho nó 'trông' như đấm bốc.

Tôi cũng sẽ tránh xa việc lưu trữ từng giá trị trong một Dictionary - vấn đề bộ nhớ của bạn sẽ tồi tệ hơn. Đây là một khung boxing có thể hữu ích.

public class Box 
{ 
    internal Box() 
    { } 

    public static Box<T> ItUp<T>(T value) 
     where T : struct 
    { 
     return value; 
    } 

    public static T ItOut<T>(object value) 
     where T : struct 
    { 
     var tbox = value as Box<T>; 
     if (!object.ReferenceEquals(tbox, null)) 
      return tbox.Value; 
     else 
      return (T)value; 
    } 
} 

public sealed class Box<T> : Box 
    where T : struct 
{ 
    public static IEqualityComparer<T> EqualityComparer { get; set; } 
    private static readonly ConcurrentStack<Box<T>> _cache = new ConcurrentStack<Box<T>>(); 
    public T Value 
    { 
     get; 
     private set; 
    } 

    static Box() 
    { 
     EqualityComparer = EqualityComparer<T>.Default; 
    } 

    private Box() 
    { 

    } 

    ~Box() 
    { 
     if (_cache.Count < 4096) // Note this will be approximate. 
     { 
      GC.ReRegisterForFinalize(this); 
      _cache.Push(this); 
     } 
    } 

    public static implicit operator Box<T>(T value) 
    { 
     Box<T> box; 
     if (!_cache.TryPop(out box)) 
      box = new Box<T>(); 
     box.Value = value; 
     return box; 
    } 

    public static implicit operator T(Box<T> value) 
    { 
     return ((Box<T>)value).Value; 
    } 

    public override bool Equals(object obj) 
    { 
     var box = obj as Box<T>; 
     if (!object.ReferenceEquals(box, null)) 
      return EqualityComparer.Equals(box.Value, Value); 
     else if (obj is T) 
      return EqualityComparer.Equals((T)obj, Value); 
     else 
      return false; 
    } 

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

    public override string ToString() 
    { 
     return Value.ToString(); 
    } 
} 

// Sample usage: 
var boxed = Box.ItUp(100); 
LegacyCode(boxingIsFun); 
void LegacyCode(object boxingIsFun) 
{ 
    var value = Box.ItOut<int>(boxingIsFun); 
} 

Nói chung, bạn nên đặt câu hỏi khác - và hỏi lời khuyên về cách loại bỏ vấn đề quyền anh này.

0

Tôi chỉ viết blog về việc triển khai bộ nhớ cache hộp mà chúng tôi sử dụng trong công cụ cơ sở dữ liệu đối tượng của chúng tôi. Các số thập phân có nhiều giá trị như bộ nhớ đệm sẽ không hiệu quả lắm, nhưng nếu bạn thấy mình nhất thiết phải sử dụng các trường đối tượng hoặc mảng đối tượng [] để lưu trữ nhiều giá trị chung, điều này có thể hữu ích:

http://www.singulink.com/CodeIndex/post/value-type-box-cache

Mức sử dụng bộ nhớ của chúng tôi giảm xuống như điên sau khi sử dụng. Về cơ bản tất cả mọi thứ trong cơ sở dữ liệu được lưu trữ trong mảng [] đối tượng và có thể có nhiều GB dữ liệu vì vậy điều này cực kỳ có lợi.

Có ba phương pháp chính bạn sẽ muốn sử dụng:

  • object BoxCache<T>.GetBox(T value) - Gets một hộp cho giá trị nếu một được lưu trữ nếu không nó hộp điều đó cho bạn.
  • object BoxCache<T>.GetOrAddBox(T value) - Nhận một hộp cho giá trị nếu một là được lưu trong bộ nhớ cache nếu không, nó sẽ thêm một ô vào bộ nhớ cache và trả về giá trị đó.
  • void AddValues<T>(IEnumerable<T> values) - Thêm hộp cho các giá trị được chỉ định vào bộ nhớ cache.
Các vấn đề liên quan