我犯了一个错误,您能指出吗?(结论)
其實(shí)許多朋友已經(jīng)在回復(fù)中發(fā)現(xiàn)問題所在了,其中最早指出錯(cuò)誤的是狼Robot同學(xué),他說:
每個(gè)T都會(huì)使用一個(gè)新的連接。
泛型類中的靜態(tài)變量會(huì)因?yàn)門的不同而產(chǎn)生不同的值,也就是說每個(gè)T所訪問的靜態(tài)變量都是獨(dú)立的。
正是這個(gè)原因,導(dǎo)致UserRepository和ArticleRepository,雖然似乎都繼承了Repository<T>類,但是因?yàn)槭褂昧瞬煌腡類型,所以實(shí)際上它們是不同的類,而它們的ConnectionKey值是不同的。使用不同的ConnectionKey,就無法從ResourceManager中獲得同一個(gè)Connection對象了。以下的代碼可以很輕易地證明這一點(diǎn)。
public static class MyClass<T> {public static readonly Guid Key = Guid.NewGuid(); }class Program {static void Main(string[] args){Console.WriteLine("int: " + MyClass<int>.Key);Console.WriteLine("string: " + MyClass<string>.Key);Console.ReadLine();} }由于MyClass<int>和MyClass<string>是不同的類,因此它們的Key也是分離的,值也不同(Guid.NewGuid()了兩次)。因此,在泛型類中定義靜態(tài)字段的時(shí)候一定要注意:不同泛型參數(shù)生成的具體類(無論是值類型還是引用類型),它們的靜態(tài)字段是獨(dú)立的。這點(diǎn)說起來簡單,但是有時(shí)候不太容易意識到。例如,我之所以犯這個(gè)錯(cuò)誤,正是因?yàn)樵綬epository類是非泛型的,而后面由于某些原因才將其改為泛型。這樣的錯(cuò)誤使用單元測試也很難檢查出來,非常隱蔽。
不過解決方案也非常簡單,例如隨意給出一個(gè)具體的Guid,而不是每次都使用Guid.NewGuid生成新的值:
public abstract class Repository<T> {private readonly static Guid ConnectionKey = new Guid("a18b2f49-cafc-43e3-a49d-3fac91701394"); }這樣,雖然UserRepostory和ArticleReposityr的ConnectionKey還是不同的Guid對象,但是它們的“值”是相同的(也就是說GetHashCode相同,Equals返回true),對字典來說它們是相同的“鍵”。當(dāng)然,還有其他解決方案,例如把ConnectionKey放到其它非泛型的類中去即可。
有些朋友還提出了其他的觀點(diǎn)。例如,ResourceManager是不同的實(shí)例,怎么做到“保留Connection對象”呢?其實(shí)只需要它們都基于一個(gè)合適的數(shù)據(jù)容器就可以了,比如都基于HttpContext.Current。這方面的例子很多,比如不同的Connection對象都是訪問同一個(gè)數(shù)據(jù)庫的。因此,這里不是問題。
還有,有朋友認(rèn)為共享Connection對象的做法不好。其實(shí)這也是沒有關(guān)系的,因?yàn)檫@里“共享”的范圍只是“單個(gè)請求”。對于ASP.NET請求來說,這些操作都是同步的,因此不會(huì)產(chǎn)生線程安全的問題。而一個(gè)請求的時(shí)間很短,因此Connection的生命周期也不長。這樣的實(shí)踐很多,例如NHiberante推薦為每個(gè)請求分配一個(gè)唯一的ISession對象(Sharp Architecture就是怎么做的)——這就相當(dāng)于一個(gè)Connection——不過我不喜歡,因此我使用的做法是為單個(gè)請求按需創(chuàng)建多個(gè)Session,但是共享一個(gè)Connection對象。此外,共享Connection對象還有其他一些好處,例如不會(huì)引發(fā)需要MSDTC的分布式事務(wù)。
這個(gè)問題已經(jīng)解決了。但是上文的評論中還有其他一些討論。例如,您知道為什么下面的代碼中,兩個(gè)時(shí)間是相同的嗎?
public static class MyClass<T> {public static readonly DateTime Time = DateTime.Now; }class Program {static void Main(string[] args){Console.WriteLine("int: " + MyClass<int>.Time);Thread.Sleep(3000);Console.WriteLine("string: " + MyClass<string>.Time);Console.ReadLine();} }它們輸出的結(jié)果是:
int: 2009/9/8 15:30:06 string: 2009/9/8 15:30:06這和我們的理解好像不同,因?yàn)楫?dāng)我們訪問MyClass<string>的時(shí)候,應(yīng)該比MyClass<int>要晚3秒鐘,但為什么時(shí)間是相同的呢?那么我們把測試代碼換一種寫法,會(huì)更清楚一些:
public static class MyClass<T> {public static readonly DateTime Time = GetNow();private static DateTime GetNow(){Console.WriteLine("GetNow execute!");return DateTime.Now;} }class Program {static void Main(string[] args){Console.WriteLine("Main execute!");Console.WriteLine("int: " + MyClass<int>.Time);Thread.Sleep(3000);Console.WriteLine("string: " + MyClass<string>.Time);Console.ReadLine();} }我們增加了會(huì)輸出一些內(nèi)容的GetNow靜態(tài)方法,Main方法的開頭也打印出一些內(nèi)容。這段代碼輸出如下:
GetNow execute! GetNow execute! Main execute! int: 2009/9/8 15:34:31 string: 2009/9/8 15:34:31可以發(fā)現(xiàn),在Main方法執(zhí)行之前,MyClass<int>和MyClass<string>的GetNow就被調(diào)用了。因此,它們的Time字段是相同的。不過,如果我們在MyClass<>中增加一個(gè)空的靜態(tài)構(gòu)造函數(shù),結(jié)果就會(huì)有所不同:
public static class MyClass<T> {public static readonly DateTime Time = GetNow();private static DateTime GetNow(){Console.WriteLine("GetNow execute!");return DateTime.Now;}static MyClass() { } }輸出如下:
Main execute! GetNow execute! int: 2009/9/8 15:40:12 GetNow execute! string: 2009/9/8 15:40:15由于GetNow方法只在“第一次”用到MyClass<int>和MyClass<string>時(shí)執(zhí)行,因此獲得的時(shí)間是不同的。不過,為什么加入了靜態(tài)構(gòu)造函數(shù)之后,Time字段的初始化時(shí)機(jī)就有所改變呢?那是因?yàn)镮L中beforefieldinit修飾在作怪。關(guān)于這一點(diǎn),許多書中都有提及。園子中的Artech同學(xué)對這個(gè)問題也有所分析。
在目前的情況下,泛型類這一性質(zhì)給我們造成了一定的麻煩。但是,只要我們使用得當(dāng),它也可以在某些場景下簡化開發(fā)。因此,最后請大家和我一起在心中默念:信腦袋,得永生,信腦袋,得永生……
from: http://blog.zhaojie.me/2009/09/i-made-a-mistake-can-you-figure-it-out-answer.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的我犯了一个错误,您能指出吗?(结论)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我犯了一个错误,您能指出吗?
- 下一篇: 您能看出这个生成缩略图的方法有什么问题吗