2015-09-07 43 views
1

我有图片上传洪水限制器:结构不缓存,但该类不

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通过Val和Classes byRef有关?

+4

你刚刚发现为什么制作可变结构是一个不好的做法。 –

回答

4

当你阅读从字典中的结构,你得到的结构的副本。 OnFileUploaded方法对值的更改只会影响副本,而不会影响字典中的原始内容。

如果您使用结构,你需要写拷贝回词典:

ImageUploadLimits copy = dict[ipAddress]; 
copy.OnFileUploaded(sizeBytes); 
dict[ipAddress] = copy; 

当您使用一个类时,你会得到从词典中引用的副本。仍然只有一个对象,但是只有两个对象。


注:术语参照通过值参考参数的传递。参数总是按值传递不论它是一个结构或类,除非你使用refout关键字。按值传递的是结构的副本或对象引用的副本。

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);