Struct不会缓存,但Class会缓存

时间:2015-09-07 15:29:21

标签: c# asp.net class caching struct

我有图片上传的限制器:

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有关?

2 个答案:

答案 0 :(得分:4)

当您从字典中读取结构时,您将获得结构的副本。 OnFileUploaded方法对值的更改仅影响副本,而不影响字典中的原始文件。

如果使用结构,则需要将副本写回字典:

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

使用类时,您将从字典中获取引用的副本。仍然只有一个对象,但有两个对象。

注意:引用按值指的是参数的传递方式。除非您使用refout关键字,否则参数始终按值传递,无论它是结构还是类。通过值传递的内容是结构的副本或对象引用的副本。

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