2009-02-02 31 views
22

Theo ý kiến ​​của bạn, tốt hơn là trả lại luồng bộ nhớ mới được cấp phát từ một hàm hoặc chuyển nó vào hàm? Ví dụ,Trả lại luồng bộ nhớ từ hàm

void Foo(MemoryStream m) 
{ 
    m.Write(somebuffer, 0, somebuffer.Length); 
} 

hoặc

void MemoryStream Foo() 
{ 
    MemoryStream retval = new MemoryStream(); 
    retval.Write(somebuffer, 0, somebuffer.Length); 
    return retval; 
} 
+2

Có rất nhiều câu trả lời hay ở đây, nhưng không có câu trả lời nào giải thích tại sao VS 2010 đưa ra cảnh báo độ tin cậy CA2000 cho biểu mẫu thứ hai. Bạn không thể xử lý rất tốt đối tượng mà bạn dự định quay trở lại và để vứt bỏ nó sau khi nó được trả về có vẻ khó xử. Làm cho tôi nghĩ rằng hình thức đầu tiên được ưa thích. –

Trả lời

17

Điều này giống như hỏi xem bạn có nên trả về chuỗi từ phương thức hoặc lấy StringBuilder và nối thêm vào chuỗi đó hay không. Câu trả lời phụ thuộc vào trường hợp sử dụng là gì.

Có khả năng người gọi sẽ muốn gọi phương thức của bạn bằng luồng hiện có chứa một số dữ liệu? Có thể họ muốn gọi nó nhiều lần bằng cách sử dụng cùng một luồng? Nếu vậy, phiên bản dùng MemoryStream sẽ hiệu quả hơn. Mặt khác, nếu họ chỉ muốn dữ liệu một lần, trả về nó như là một MemoryStream (hoặc, đơn giản hơn, như là một mảng byte) cũng có thể thích hợp hơn.

Thật không may từ mô tả chúng tôi không thể thực sự biết điều gì đang xảy ra. Tất nhiên, bạn có thể thực hiện cả hai như quá tải và gọi một từ khác.

2

Bạn có thể yên tâm gửi lại từ hàm. Bạn phải gọi Dispose() hoặc đặt nó bên trong mệnh đề using vì nó triển khai IDisposable.

+3

Gọi Dispose trên MemoryStream không làm gì cả. Nó không có bất kỳ tài nguyên không được quản lý nào. –

+1

+1 cho nhận xét của Mehrdad. –

+1

@Merhdad & Jon - Không phải lúc nào cũng đúng. Nếu bạn đã sử dụng bất kỳ phương thức async nào trên luồng, nó sẽ lưu bộ nhớ đệm một WaitHandle và sau đó phương thức Dispose sẽ làm điều gì đó. Vì vậy, bạn an toàn hơn để vứt bỏ nó trừ khi bạn * biết * không có các phương thức không đồng bộ nào được, hoặc sẽ được gọi là. –

1

Câu hỏi thứ hai của bạn tốt hơn. Tôi luôn cố gắng tránh biến đổi các đối tượng bên trong các hàm nếu có thể.

+0

Các luồng được * siêu nhiên * bị đột biến đó là lý do tại sao chúng là các luồng và không phải mảng bất biến. Và cũng có thể truyền một luồng tới phương thức Lưu, hoặc Xuất, hoặc ViếtXXX là khá phổ biến trong quan điểm của tôi ... –

+0

+1 cho nhận xét của Pop –

+0

Có rất nhiều đối tượng được cho là bị biến đổi. Tuy nhiên nếu bạn định khởi tạo đối tượng hoàn toàn trong hàm tại sao không có hàm trả về tham chiếu đến đối tượng thay vì cung cấp tham chiếu? –

0

Không có hai sự khác biệt. Trong trường hợp đầu tiên người gọi sẽ có thể kiểm soát các dòng bộ nhớ và trong trường hợp thứ hai, bạn có thể làm một cái gì đó như thế này:

using (MemoryStream ms = Foo()) 
{ 
    //Do Stuff here. 
} 

Điều quan trọng nhất là phải nhớ để xử lý nó một cách chính xác.

+1

MemoryStream hoàn toàn là một đối tượng được quản lý. Gọi Dispose sẽ không làm gì cả. –

+3

@Merhdad - Không phải lúc nào cũng đúng. Nếu bạn đã sử dụng bất kỳ phương thức async nào trên luồng, nó sẽ lưu bộ nhớ đệm một WaitHandle và sau đó phương thức Dispose sẽ làm điều gì đó. Vì vậy, bạn an toàn hơn để vứt bỏ nó trừ khi bạn * biết * không có các phương thức không đồng bộ nào được, hoặc sẽ được gọi là. –

7

Chuyển luồng bộ nhớ vào một hàm và trả về một luồng bộ nhớ cho một hàm không được sử dụng thay thế cho nhau. Các phương pháp bạn mô tả phục vụ hai mục đích khác nhau.

  • Việc chuyển đến chức năng là khi bạn muốn chức năng thực hiện điều gì đó với thông số.

  • Trả lại nội dung nào đó từ một hàm là khi người gọi được yêu cầu làm điều gì đó với kết quả.

Bạn đang nói về hai thứ khác nhau, táo và cam.

3

Tôi sẽ luôn chuyển luồng vào hàm. Điều này cho phép nó hoạt động với bất kỳ luồng nào của người gọi đang chọn, ví dụ: thẳng vào một tệp mà không có bất kỳ bộ đệm nào.

+0

Sau đó, có bao nhiêu phương thức trả về luồng? ;-) Usualy có tên Open ... hoặc GetStream ... Tôi nghĩ nó phụ thuộc vào mục đích của luồng. –

+1

Vì đó là ** mở **/** tạo ** luồng. Nó không phải là rõ ràng cho dù phương pháp của bạn là tạo ra một dòng vs đẩy dữ liệu vào một. Hai điều có câu trả lời khác nhau. –

0

Tôi thích biểu mẫu được tiêm hơn. Nó loại bỏ sự liên kết trực tiếp giữa mã của bạn và MemoryStream và nó làm cho nó dễ kiểm tra hơn.

public void Foo(Stream stream) 
{ 
    stream.Write(somebuffer, 0, somebuffer.Length); 
} 

Bây giờ tôi có thể kiểm tra Foo với bất kỳ lớp nào triển khai Luồng bao gồm lớp giả.

Thông thường, tôi sẽ thực hiện việc tiêm trong hàm tạo của lớp chứ không phải trên các phương thức riêng lẻ nhưng ý tưởng về cơ bản giống nhau.

public class FooClass 
{ 
    public Stream FooStream { get; private set; } 

    public FooClass() : this(null) { } 

    public FooClass(Stream stream) 
    { 
     // provide a default if not specified 
     this.FooStream = stream ?? new MemoryStream(); 
    } 

    public void Foo() 
    { 
     this.FooStream.Write(somebuffer, 0, somebuffer.Length); 
    } 
} 
0

tôi sẽ có xu hướng hướng tới đầu tiên vì hai lý do:

  1. Ngữ nghĩa của người "sở hữu" các dòng bộ nhớ rất rõ ràng. Người gọi đã tạo ra nó, do đó, tùy thuộc vào người gọi để loại bỏ nó (đây là vấn đề với các loại luồng khác chứa tài nguyên không được quản lý). luồng của bạn

Điều đó nói rằng, nếu mục đích chính của Foo là phương pháp nhà máy cho MemoryStreams (tương tự như một cái gì đó như File.Open, v.v.), cách tiếp cận thứ hai có ý nghĩa hơn.

1

Sau một vài suy nghĩ khác, tôi nghĩ nó đi xuống đến ngữ nghĩa dự định của phương pháp Foo. Là nó:

  • Một hoạt động tạo ra một dòng (ví dụ File.Open().)
  • Một hoạt động mà sửa đổi một dòng (ví dụ something.WriteXml().)

Nếu câu trả lời là "tạo ra một dòng suối", có nó trở lại một dòng. Nếu nó sửa đổi luồng, hãy truyền luồng vào.

Nếu câu trả lời là "một số trong cả hai", thì có thể có ý nghĩa khi tách phương thức sao cho chỉ có một trách nhiệm.

0

Vì luồng là tài nguyên cần xử lý rõ ràng (bộ nhớ, tệp, mạng), cách tốt nhất là áp dụng phương pháp RAII để xử lý chúng. Điều đó có nghĩa rằng chức năng khởi tạo chúng phải chịu trách nhiệm với việc phát hành (chúng tôi đã "sử dụng" keyord trong C# chỉ cho điều đó). Để bật mẫu này, tôi nói chấp nhận luồng dưới dạng tham số. Bằng cách đó, người gọi có thể quyết định thời điểm tạo và xử lý luồng. Trong khi bạn đang ở đó, phương thức của bạn chấp nhận bất kỳ việc triển khai luồng nào; có vẻ như nó chỉ cần làm việc cho MemoryStream.

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