我有图片上传的限制器:
public class FloodLimiter
{
public static void OnImageUploaded(string ipAddress, int sizeBytes)
{
var dict = GetImageUploadFloodLimitDict();
if (!dict.ContainsKey(ipAddress))
{
dict.Add(ipAddress, new ImageUploadLimits());
}
dict[ipAddress].OnFileUploaded(sizeBytes);
}
public static bool CanUploadImages(string ipAddress)
{
var dict = GetImageUploadFloodLimitDict();
if (!dict.ContainsKey(ipAddress)) return true;
return dict[ipAddress].BelowLimits;
}
private static readonly object FloodLimitLock = new object();
/// <summary>
/// Dictionary of IP's, and total images uploaded + total image size uploaded
/// </summary>
private static Dictionary<string, ImageUploadLimits> GetImageUploadFloodLimitDict()
{
const string cacheIndex = Settings.CachePrefix + "ImageUploadFloodLimiter";
var context = HttpContext.Current;
if (context.Cache[cacheIndex] == null)
{
lock (FloodLimitLock)
{
if (context.Cache[cacheIndex] == null)
{
var dict = new Dictionary<string, ImageUploadLimits>();
context.Cache.Add(cacheIndex, dict, null, DateTime.Now.AddMinutes(Settings.ImageUpload.FloodLimitRecycleMinutes), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null);
}
}
}
return (Dictionary<string, ImageUploadLimits>)context.Cache[cacheIndex];
}
}
如果ImageUploadLimits
定义为:
private struct ImageUploadLimits
{
private int _totalImagesUploaded;
private int _totalBytesUploaded;
public bool BelowLimits { get {
return _totalImagesUploaded < Settings.ImageUpload.MaximumImagesCanUploadInPeriod && _totalBytesUploaded < (Settings.ImageUpload.MaximumImageUploadMbInPeriod * 1000000);
}}
public void OnFileUploaded(int sizeBytes)
{
_totalImagesUploaded ++;
_totalBytesUploaded += sizeBytes;
}
}
缓存永远不会更新(它总是返回上传的1张图片)。
如果我将ImageUploadLimits
定义为:
private class ImageUploadLimits
{
private int _totalImagesUploaded;
private int _totalBytesUploaded;
public bool BelowLimits { get {
return _totalImagesUploaded < Settings.ImageUpload.MaximumImagesCanUploadInPeriod && _totalBytesUploaded < (Settings.ImageUpload.MaximumImageUploadMbInPeriod * 1000000);
}}
public void OnFileUploaded(int sizeBytes)
{
_totalImagesUploaded++;
_totalBytesUploaded += sizeBytes;
}
}
一切正常!
为什么struct
版本没有缓存?是否与Structs
传递byVal和Classes
byRef有关?
答案 0 :(得分:4)
当您从字典中读取结构时,您将获得结构的副本。 OnFileUploaded
方法对值的更改仅影响副本,而不影响字典中的原始文件。
如果使用结构,则需要将副本写回字典:
ImageUploadLimits copy = dict[ipAddress];
copy.OnFileUploaded(sizeBytes);
dict[ipAddress] = copy;
使用类时,您将从字典中获取引用的副本。仍然只有一个对象,但有两个对象。
注意:引用和按值指的是参数的传递方式。除非您使用ref
或out
关键字,否则参数始终按值传递,无论它是结构还是类。通过值传递的内容是结构的副本或对象引用的副本。
答案 1 :(得分:1)
再加上Guffa的回答:如果使用不可变结构,那么更难以混淆自己,其中改变状态的方法返回一个新实例而不是修改内部状态。
这使得您(以及其他阅读代码的人)更加明显,您需要将返回的值分配回到以下位置:
struct ImageUploadLimits
{
private readonly int _totalImagesUploaded;
private readonly int _totalBytesUploaded;
public ImageUploadLimits(int totalImagesUploaded, int totalBytesUploaded)
{
_totalImagesUploaded = totalImagesUploaded;
_totalBytesUploaded = totalBytesUploaded;
}
public bool BelowLimits
{
get { return _totalImagesUploaded < Settings.ImageUpload.MaximumImagesCanUploadInPeriod && _totalBytesUploaded < (Settings.ImageUpload.MaximumImageUploadMbInPeriod * 1000000); }
}
public ImageUploadLimits WithAdditionalFileUpload(int sizeBytes)
{
return new ImageUploadLimits(_totalImagesUploaded + 1, _totalBytesUploaded + sizeBytes);
}
}
然后:
var limits = default(ImageUploadLimits);
dict.TryGetValue(ipAddress, out limits);
dict[ipAddress] = limits.WithAdditionalFileUpload(sizeBytes);