Lớp StringBuilder được triển khai như thế nào? Liệu nó có tạo nội bộ các đối tượng chuỗi mới mỗi lần chúng ta thêm vào không?Lớp StringBuilder được triển khai như thế nào? Liệu nó có tạo nội bộ các đối tượng chuỗi mới mỗi lần chúng ta thêm vào không?
Trả lời
Trong .NET 2.0 nó sử dụng lớp String
nội bộ. String
chỉ là không thay đổi bên ngoài không gian tên System
, vì vậy StringBuilder
có thể làm điều đó.
Trong .NET 4.0 String
đã được thay đổi để sử dụng char[]
.
Trong 2,0 StringBuilder
trông như thế này
public sealed class StringBuilder : ISerializable
{
// Fields
private const string CapacityField = "Capacity";
internal const int DefaultCapacity = 0x10;
internal IntPtr m_currentThread;
internal int m_MaxCapacity;
internal volatile string m_StringValue; // HERE ----------------------
private const string MaxCapacityField = "m_MaxCapacity";
private const string StringValueField = "m_StringValue";
private const string ThreadIDField = "m_currentThread";
Nhưng trong 4,0 nó trông như thế này:
public sealed class StringBuilder : ISerializable
{
// Fields
private const string CapacityField = "Capacity";
internal const int DefaultCapacity = 0x10;
internal char[] m_ChunkChars; // HERE --------------------------------
internal int m_ChunkLength;
internal int m_ChunkOffset;
internal StringBuilder m_ChunkPrevious;
internal int m_MaxCapacity;
private const string MaxCapacityField = "m_MaxCapacity";
internal const int MaxChunkSize = 0x1f40;
private const string StringValueField = "m_StringValue";
private const string ThreadIDField = "m_currentThread";
Vì vậy, rõ ràng nó đã được thay đổi từ việc sử dụng một string
để sử dụng một char[]
.
EDIT: Đã cập nhật câu trả lời để phản ánh các thay đổi trong .NET 4 (mà tôi chỉ mới phát hiện ra).
Không có ý tưởng .. Hãy suy nghĩ Im sẽ làm một số ma thuật phản xạ để đáp ứng sự tò mò của tôi :) – cwap
@Brian: theo như tôi biết nó giữ một mảng 'Char' nội bộ, không phải là một' String' (ít nhất là trong .NET 4, có lẽ điều này đã thay đổi?) –
@ Fredrik - trong việc triển khai MS, nó thực sự là một chuỗi 'bị biến đổi –
Không thực sự - nó sử dụng bộ đệm ký tự bên trong. Chỉ khi dung lượng bộ đệm bị cạn kiệt, nó sẽ cấp phát bộ đệm mới. Nối thêm hoạt động sẽ đơn giản thêm vào bộ đệm này, đối tượng chuỗi sẽ được tạo ra khi phương thức ToString() được gọi trên nó - từ nay trở đi, nó được khuyến khích cho nhiều chuỗi nối vì mỗi chuỗi concat truyền thống sẽ tạo chuỗi mới. Bạn cũng có thể chỉ định dung lượng ban đầu cho trình tạo chuỗi nếu bạn có ý tưởng thô về nó để tránh phân bổ nhiều lần.
Chỉnh sửa: Mọi người đang chỉ ra rằng sự hiểu biết của tôi là sai. Vui lòng bỏ qua câu trả lời (Tôi không xóa nó - nó sẽ đứng như một bằng chứng về sự thiếu hiểu biết của tôi :-)
Nó hoạt động * như thể * nó là một bộ đệm ký tự, nhưng nó thực sự là một thể hiện 'chuỗi' bị biến đổi. Thật thà. –
Cảm ơn Marc - Tôi đã bị ấn tượng rằng nó sử dụng bộ đệm ký tự. Nó có nghĩa là nó sẽ có một số thực hiện bản địa để thay đổi đối tượng chuỗi. – VinayC
chắc chắn, nhưng nó là một lớp khung cốt lõi. Nó có quyền truy cập vào cài đặt gốc. –
Nếu tôi nhìn vào NET Reflector tại NET 2 sau đó tôi sẽ tìm thấy điều này:
public StringBuilder Append(string value)
{
if (value != null)
{
string stringValue = this.m_StringValue;
IntPtr currentThread = Thread.InternalGetCurrentThread();
if (this.m_currentThread != currentThread)
{
stringValue = string.GetStringForStringBuilder(stringValue, stringValue.Capacity);
}
int length = stringValue.Length;
int requiredLength = length + value.Length;
if (this.NeedsAllocation(stringValue, requiredLength))
{
string newString = this.GetNewString(stringValue, requiredLength);
newString.AppendInPlace(value, length);
this.ReplaceString(currentThread, newString);
}
else
{
stringValue.AppendInPlace(value, length);
this.ReplaceString(currentThread, stringValue);
}
}
return this;
}
Vì vậy, nó là một ví dụ chuỗi biến đổi ...
EDIT Ngoại trừ trong .NET 4 nó là a char[]
@Richard: cảm ơn EDIT. Không biết thực tế đó. –
Nếu bạn muốn xem một trong những triển khai có thể xảy ra (Điều này tương tự như việc triển khai thực hiện microsoft đến v3.5), bạn có thể thấy the source of the Mono one trên github.
Tôi đã thực hiện một mẫu nhỏ để chứng minh như thế nào StringBuilder làm việc trong .NET 4. Hợp đồng là
public interface ISimpleStringBuilder
{
ISimpleStringBuilder Append(string value);
ISimpleStringBuilder Clear();
int Lenght { get; }
int Capacity { get; }
}
Và đây là một thực hiện rất cơ bản
public class SimpleStringBuilder : ISimpleStringBuilder
{
public const int DefaultCapacity = 32;
private char[] _internalBuffer;
public int Lenght { get; private set; }
public int Capacity { get; private set; }
public SimpleStringBuilder(int capacity)
{
Capacity = capacity;
_internalBuffer = new char[capacity];
Lenght = 0;
}
public SimpleStringBuilder() : this(DefaultCapacity) { }
public ISimpleStringBuilder Append(string value)
{
char[] data = value.ToCharArray();
//check if space is available for additional data
InternalEnsureCapacity(data.Length);
foreach (char t in data)
{
_internalBuffer[Lenght] = t;
Lenght++;
}
return this;
}
public ISimpleStringBuilder Clear()
{
_internalBuffer = new char[Capacity];
Lenght = 0;
return this;
}
public override string ToString()
{
//use only non-null ('\0') characters
var tmp = new char[Lenght];
for (int i = 0; i < Lenght; i++)
{
tmp[i] = _internalBuffer[i];
}
return new string(tmp);
}
private void InternalExpandBuffer()
{
//double capacity by default
Capacity *= 2;
//copy to new array
var tmpBuffer = new char[Capacity];
for (int i = 0; i < _internalBuffer.Length; i++)
{
char c = _internalBuffer[i];
tmpBuffer[i] = c;
}
_internalBuffer = tmpBuffer;
}
private void InternalEnsureCapacity(int additionalLenghtRequired)
{
while (Lenght + additionalLenghtRequired > Capacity)
{
//not enough space in the current buffer
//double capacity
InternalExpandBuffer();
}
}
}
Mã này được không thread- an toàn, không thực hiện bất kỳ xác thực đầu vào nào và không sử dụng ma thuật bên trong (không an toàn) của System.String. Tuy nhiên nó thể hiện ý tưởng đằng sau lớp StringBuilder.
Một số kiểm tra đơn vị và mã mẫu đầy đủ có thể được tìm thấy tại github.
Câu trả lời được chấp nhận đã bỏ lỡ dấu mốc một dặm.Thay đổi đáng kể đối với StringBuilder
trong 4.0 không phải là thay đổi từ một không an toàn string
thành char[]
- thực tế là StringBuilder
là hiện tại là danh sách liên kết gồm StringBuilder
trường hợp.
Lý do cho sự thay đổi này nên được rõ ràng: hiện nay có bao giờ là một nhu cầu để phân bổ lại bộ đệm (một hoạt động tốn kém, từ đó, cùng với việc phân bổ bộ nhớ hơn, bạn cũng phải sao chép tất cả các nội dung từ bộ đệm cũ sang bộ đệm mới).
Điều này có nghĩa là gọi ToString()
hiện chậm hơn một chút, vì chuỗi cuối cùng cần được tính toán, nhưng thực hiện số lượng lớn các hoạt động Append()
hiện tại là đáng kể nhanh hơn. Điều này phù hợp với trường hợp sử dụng thông thường cho StringBuilder
: nhiều cuộc gọi đến Append()
, theo sau là một cuộc gọi đến ToString()
.
Bạn có thể tìm thấy điểm chuẩn here. Kết luận? Danh sách liên kết mới StringBuilder
sử dụng nhiều bộ nhớ hơn, nhưng nhanh hơn đáng kể đối với trường hợp sử dụng điển hình.
- 1. UNITY: chuyển vào nội dung dữ liệu mới mỗi lần?
- 2. Lớp Object được triển khai như thế nào (các phương thức như hashCode và các trường nội bộ)?
- 3. Khai báo đối tượng Java với tên kiểu/lớp của nó như là chuỗi
- 4. Các tham chiếu yếu được triển khai như thế nào?
- 5. Tại sao chúng ta không thể tạo đối tượng ostream của chúng ta
- 6. Tôi có thể linh hoạt thêm dữ liệu vào các đối tượng Moose như thế nào?
- 7. PrintStream đối tượng ra được khởi tạo bằng null, làm thế nào chúng ta gọi phương pháp trên nó?
- 8. Các lượt xem PostgreSQL có được tạo mới mỗi khi chúng được truy vấn không?
- 9. Các mảng được triển khai trong Perl như thế nào?
- 10. Có một thể hiện của siêu lớp được tạo khi chúng ta khởi tạo một đối tượng không?
- 11. OpenID được triển khai như thế nào?
- 12. Chúng ta có thể xem các đối tượng trong bộ nhớ JVM không?
- 13. nội dung javascript: sự kiện được triển khai như thế nào?
- 14. Các bộ so sánh có nên được khởi tạo mỗi lần hoặc chỉ một lần?
- 15. Bảng băm được triển khai nội bộ bằng các ngôn ngữ phổ biến như thế nào?
- 16. là nó có thể tạo hồ bơi đối tượng tương tự như chuỗi?
- 17. Các mảng được triển khai trong java như thế nào?
- 18. HttpSession được triển khai như thế nào?
- 19. Các khối try/catch được triển khai như thế nào?
- 20. Chuỗi string.find được triển khai như thế nào trong CPython?
- 21. BinaryFormatter.Deserialize tạo đối tượng mới như thế nào?
- 22. Lớp này triển khai IDisposable như thế nào nếu nó không có phương thức Vứt bỏ?
- 23. IO không chặn được triển khai như thế nào?
- 24. Có cách nào để thêm siêu dữ liệu vào các đối tượng JavaScript không?
- 25. Trang chuyên sâu Ajax: sử dụng lại cùng một đối tượng XMLHttpRequest hoặc tạo một đối tượng mới mỗi lần?
- 26. Groovy thêm các phương thức mới vào các lớp Java như thế nào?
- 27. Thêm tập tin vào một lần nhấp khi triển khai
- 28. Lớp Java có được khởi tạo bởi chuỗi sử dụng nó lần đầu tiên không?
- 29. Lớp chuỗi Python như StringBuilder trong C#?
- 30. Có thể một đối tượng Ngoại lệ tăng thêm ngoại lệ khác do lỗi nội bộ của nó không?
+1 Tôi cũng đã học được điều gì đó mới từ câu hỏi này :) –
@Brian Rasmussen chờ câu trả lời của Jon Skeet. Tôi đặt cược nó sẽ là rất lớn và đầy đủ các công cụ mới để tìm hiểu;) – prostynick
Reflector tiết lộ tất cả. –