这个问题是我在MSDN中文技术论坛中遇到的,现象十分诡异,仔细的研究了一下源代码,现在分享给大家,大家先看一段Demo:
FileStream fs = new FileStream(@"D:\aa.jpg", FileMode.Open, FileAccess.Read);
Image image = Image.FromStream(fs);
image.Tag = "txtbmp";
ImageList bigHeadPicList = new ImageList();
bigHeadPicList.Images.Add("1",image);
Console.WriteLine(bigHeadPicList.Images["1"].Tag); //这里Tag是null,相当意外!
直观上感觉image和 bigHeadPicList.Images["1"]并不是同一对象,让我们通过源代码来验证这一点:
先看ImageCollection 类的索引器的源代码:
public Image this[string key] {
get {
// We do not support null and empty string as valid keys.
if ((key == null) || (key.Length == 0)){
return null;
}
// Search for the key in our collection
int index = IndexOfKey(key);
if (IsValidIndex(index)) {
return this[index];
}
else {
return null;
}
}
}
关键的实现都在另外一个索引器中:
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Image this[int index] {
get {
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
return owner.GetBitmap(index); //这行代码比较关键
}
set {
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
if (value == null) {
throw new ArgumentNullException("value");
}
if (!(value is Bitmap))
throw new ArgumentException(SR.GetString(SR.ImageListBitmap));
AssertInvariant();
Bitmap bitmap = (Bitmap)value;
bool ownsImage = false;
if (owner.UseTransparentColor) {
// Since there's no ImageList_ReplaceMasked, we need to generate
// a transparent bitmap
Bitmap source = bitmap;
bitmap = (Bitmap) bitmap.Clone();
bitmap.MakeTransparent(owner.transparentColor);
ownsImage = true;
}
try {
IntPtr hMask = ControlPaint.CreateHBitmapTransparencyMask(bitmap);
IntPtr hBitmap = ControlPaint.CreateHBitmapColorMask(bitmap, hMask);
bool ok = SafeNativeMethods.ImageList_Replace(new HandleRef(owner, owner.Handle), index, new HandleRef(null, hBitmap), new HandleRef(null, hMask));
SafeNativeMethods.DeleteObject(new HandleRef(null, hBitmap));
SafeNativeMethods.DeleteObject(new HandleRef(null, hMask));
if (!ok)
throw new InvalidOperationException(SR.GetString(SR.ImageListReplaceFailed));
} finally {
if(ownsImage) {
bitmap.Dispose();
}
}
}
}
这行代码比较关键:
return owner.GetBitmap(index);
下面我们看看GetBitmap方法的实现:
private Bitmap GetBitmap(int index) {
if (index < 0 || index >= Images.Count)
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
Bitmap result=null;
if(ColorDepth == ColorDepth.Depth32Bit) {
NativeMethods.IMAGEINFO imageInfo = new NativeMethods.IMAGEINFO(); // review? do I need to delete the mask and image inside of imageinfo?
if(SafeNativeMethods.ImageList_GetImageInfo(new HandleRef(this, this.Handle), index, imageInfo)) {
Bitmap tmpBitmap = null;
BitmapData bmpData = null;
BitmapData targetData = null;
IntSecurity.ObjectFromWin32Handle.Assert();
try {
tmpBitmap = Bitmap.FromHbitmap(imageInfo.hbmImage);
//
bmpData = tmpBitmap.LockBits(new Rectangle(imageInfo.rcImage_left,imageInfo.rcImage_top, imageInfo.rcImage_right-imageInfo.rcImage_left, imageInfo.rcImage_bottom-imageInfo.rcImage_top), ImageLockMode.ReadOnly, tmpBitmap.PixelFormat);
int offset = bmpData.Stride * imageSize.Height * index;
// we need do the following if the image has alpha because otherwise the image is fully transparent even though it has data
if(BitmapHasAlpha(bmpData)) {
result = new Bitmap(imageSize.Width, imageSize.Height, PixelFormat.Format32bppArgb); //注意这里是新创建的Bitmap实例
targetData = result.LockBits(new Rectangle(0, 0, imageSize.Width, imageSize.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
CopyBitmapData(bmpData, targetData);//仅仅拷贝图像数据
}
} finally {
CodeAccessPermission.RevertAssert();
if(tmpBitmap != null && bmpData != null) {
tmpBitmap.UnlockBits(bmpData);
}
if(result != null && targetData != null) {
result.UnlockBits(targetData);
}
}
}
}
if(result == null) { // paint with the mask but no alpha...
result = new Bitmap(imageSize.Width, imageSize.Height); //新创建Bitmap实例
Graphics graphics = Graphics.FromImage(result);
try {
IntPtr dc = graphics.GetHdc();
try {
SafeNativeMethods.ImageList_DrawEx(new HandleRef(this, Handle), index, new HandleRef(graphics, dc), 0, 0,
imageSize.Width, imageSize.Height, NativeMethods.CLR_NONE, NativeMethods.CLR_NONE, NativeMethods.ILD_TRANSPARENT);
}
finally {
graphics.ReleaseHdcInternal(dc);
}
}
finally {
graphics.Dispose();
}
}
// gpr: See Icon for description of fakeTransparencyColor
result.MakeTransparent(fakeTransparencyColor);
return result;
}
代码比较长,关键的部分我使用注释标记出来了,大家看我标记的注释就可以了,大家可以发现,无论BitmapHasAlpha是否为真,都是新创建Bitmap对象然后返回的,所以在mage和 bigHeadPicList.Images["1"]并不是同一对象,所以Tag是null的!