檢查對SO的earlier question,我開始思考其中類公開的值,如集合,由值的類型實現的接口的情況。在下面的代碼示例中,我使用了一個List,並將該列表公開爲IEnumerable。從接口投放到基礎類型
通過IEnumerable接口顯露列表定義該列表僅列舉以上,未改性的意圖。但是,由於實例可以重新轉換回列表,因此列表本身當然可以修改。
我還包括在樣品中的一個版本,通過各方法被調用時複製列表項的引用到一個新的列表,從而防止改變底層列表可防止變形例的方法的。
所以我的問題是,應該所有的代碼暴露的具體類型實現接口的複製操作的方式做到這一點?會有一個語言結構,明確表示「我想通過一個接口來揭露這個值,並調用代碼應該只能夠通過接口來使用這個值」的價值?其他人使用什麼技術來防止在通過接口公開具體值時出現這些意外副作用。
請注意,我明白,所示的行爲是預期的行爲。我並不是聲稱這種行爲是錯誤的,只是它確實允許使用除表達的意圖以外的功能。也許我爲界面分配了太多的意義 - 把它看作一個功能約束。思考?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TypeCastTest
{
class Program
{
static void Main(string[] args)
{
// Demonstrate casting situation
Automobile castAuto = new Automobile();
List<string> doorNamesCast = (List<string>)castAuto.GetDoorNamesUsingCast();
doorNamesCast.Add("Spare Tire");
// Would prefer this prints 4 names,
// actually prints 5 because IEnumerable<string>
// was cast back to List<string>, exposing the
// Add method of the underlying List object
// Since the list was cast to IEnumerable before being
// returned, the expressed intent is that calling code
// should only be able to enumerate over the collection,
// not modify it.
foreach (string doorName in castAuto.GetDoorNamesUsingCast())
{
Console.WriteLine(doorName);
}
Console.WriteLine();
// --------------------------------------
// Demonstrate casting defense
Automobile copyAuto = new Automobile();
List<string> doorNamesCopy = (List<string>)copyAuto.GetDoorNamesUsingCopy();
doorNamesCopy.Add("Spare Tire");
// This returns only 4 names,
// because the IEnumerable<string> that is
// returned is from a copied List<string>, so
// calling the Add method of the List object does
// not modify the underlying collection
foreach (string doorName in copyAuto.GetDoorNamesUsingCopy())
{
Console.WriteLine(doorName);
}
Console.ReadLine();
}
}
public class Automobile
{
private List<string> doors = new List<string>();
public Automobile()
{
doors.Add("Driver Front");
doors.Add("Passenger Front");
doors.Add("Driver Rear");
doors.Add("Passenger Rear");
}
public IEnumerable<string> GetDoorNamesUsingCopy()
{
return new List<string>(doors).AsEnumerable<string>();
}
public IEnumerable<string> GetDoorNamesUsingCast()
{
return doors.AsEnumerable<string>();
}
}
}
你可以使用反射來打破其他規則並訪問私有成員。國際海事組織,你已經宣佈它是一個'IEnumerable';如果有人想「欺騙」並將其轉換爲您未定義爲公共API的一部分,那麼這就是他們的業務 - 以及風險。 – 2010-10-29 21:27:10
@Kirk Woll - 將反射作爲「違反規則」的一種手段,這是一個很好的觀點。作爲這個問題的一部分,我確實希望其他人關於是否將界面轉換爲具體類型甚至構成了「違反規則」的形式,或者如果大多數人認爲這是API設計中應該考慮的預期選項,那麼他們的反饋意見。感謝您的評論。 – JeremyDWill 2010-10-29 21:36:03