.NET Framework - Cache (缓存) - System.Runtime.Caching

创建时间:
2015-12-01 22:15
最近更新:
2018-10-14 18:25

Brief - 网摘

  • .NET 4.0 的缓存功能主要由三部分组成: System.Runtime.Caching、System.Web.Caching.Cache、Output Cache。
  • Output Cache 则是 ASP.NET 里面使用的,在 ASP.NET 4.0 之前的版本都是直接使用 System.Web.Caching.Cache 来缓存 HTML 片段。在 ASP.NET 4.0 中对它进行了重新设计,提供了一个 OutputCacheProvider 供开发人员进行扩展,但是它默认情况下,仍然使用 System.Web.Caching.Cache 来做做缓存。

Resource - microsoft.com

  1. .NET Framework 应用程序中的缓存 - In the .NET Framework 3.5 and earlier versions, ASP.NET provided an in-memory cache implementation in the System.Web.Caching namespace. In previous versions of the .NET Framework, caching was available only in the System.Web namespace and therefore required a dependency on ASP.NET classes. In the .NET Framework 4, the System.Runtime.Caching namespace contains APIs that are designed for both Web and non-Web applications.
  1. 在 ASP.NET Core 中缓存响应 - Entrance
  2. ASP.NET 缓存概述 - Entrance, 2007-11
  1. System.Runtime.Caching 命名空间 - 包含使您可以在 .NET Framework 应用程序中实现缓存的类型
  2. System.Runtime.Caching.ObjectCache 类
  3. System.Runtime.Caching.MemoryCache 类 - The built-in MemoryCache class derives from the ObjectCache class. The MemoryCache class is the only concrete object cache implementation in the .NET Framework 4 that derives from the ObjectCache class. MemoryCache 类 不允许 缓存中的值为 null。任何尝试 添加或改变 某个缓存项 为 null 值 将失败。
  4. System.Runtime.Caching.MemoryCache.Default 属性 - This property always returns a reference to the default cache instance. For typical application scenarios, only one instance of MemoryCache is required (对于典型应用场景,只需一个 MemoryCache 实例即可。TonyNote: 通常用该属性即可,无需再 new 实例). Because the default cache instance is not created by the constructor, you must use configuration to explicitly set the memory and polling values for the default cache instance. For more information, see <memoryCache> Element (Cache Settings) (因为 该属性底层的默认实例 不由构造函数创建,必须使用配置来 为默认实例 显式设置 内存和轮询值。有关详细信息,请参阅 <memoryCache>元素 (缓存设置)). 由源码可见,该属性底层即一个 this._nameDefault 的 静态实例。
  5. System.Runtime.Caching.CacheItemPriority 枚举 - 指定用于确定是否逐出某个缓存项的优先级别设置。 该枚举共 2 个元素: 0/Default 指示移除缓存项没有优先级; 1/NotRemovable 指示绝不应从缓存中移除某个缓存项。
  6. 演练: 在 ASP.NET 中缓存应用程序数据 - 基于 System.Runtime.Caching.MemoryCache.Default
  1. System.Web.Caching 命名空间 - provides classes for caching frequently used data on the server.
  2. System.Web.Caching.Cache 类 - Implements the cache for a Web application.
  3. System.Web.Caching.OutputCache 类
  4. System.Web.Caching.CacheDependency 类

Resource - MemoryCache

  1. 深入理解 .NET MemoryCache
  2. 使用 MemoryCache
  3. C# 缓存 - 错误百出,浪费时间
  4. Winform 里的缓存使用

System.Runtime.Caching.MemoryCache 类 versus System.Web.Caching.Cache 类

原文:
The MemoryCache class is similar to the ASP.NET Cache class. The MemoryCache class has many properties and methods for accessing the cache that will be familiar to you if you have used the ASP.NET Cache class. The main differences between the Cache and MemoryCache classes are that the MemoryCache class has been changed to make it usable by .NET Framework applications that are not ASP.NET applications. For example, the MemoryCache class has no dependencies on the System.Web assembly. Another difference is that you can create multiple instances of the MemoryCache class for use in the same application and in the same AppDomain instance.

译文 (金山词霸):
MemoryCache类似于ASP.NETCache类。MemoryCache类具有许多用于访问缓存的属性和方法,如果您使用了ASP.NETCache类,您将熟悉这些属性和方法。Cache和MemoryCache类之间的主要区别是,MemoryCache类已被更改,使其可由非ASP.NET应用程序的.NET Framework应用程序使用。例如,MemoryCache类不依赖System.Web程序集。另一个不同之处是,您可以创建MemoryCache类的多个实例,以便在同一个应用程序和同一个AppDomain实例中使用。

-- System.Runtime.Caching.MemoryCache 类

WebForm 中的缓存

2018-10-07 发现 B250 D1526Backup\CodeAccumulation\AspNet & C#\ASP.NET缓存 有些早年整理的文档。以后如果有需要再整理至 TonyNote,不过不太可能需要了。

Code Snippet - MemoryCachePlus.GetFromCacheOrFetch() 曾使用过的代码

using Bll.CodeGen;
using System;
using System.Collections.Generic;
using System.Runtime.Caching;
using Tl.SystemPlus;

namespace Bll.Output
{
    public class Caches
    {
        #region PrjDb_All:

        private static readonly object _syncRoot_PrjDb_All = new object();
        public List<PrjDb_Bo> PrjDb_All()
        {
            var mc = new MemoryCachePlus();
            string key = "PrjDb_All";
            var policy = new CacheItemPolicy {
                AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(300)
            };
            List<PrjDb_Bo> data = mc.GetFromCacheOrFetch(
                key,
                _syncRoot_PrjDb_All,
                policy,
                new PrjDb_Bll().SltAll
            );
            return data;
        }

        #endregion PrjDb_All.

        #region ClsTb_All:

        private static readonly object _syncRoot_ClsTb_All = new object();
        public List<ClsTb_Bo> ClsTb_All(ulong idOfPrjDb)
        {
            var mc = new MemoryCachePlus();
            string key = "ClsTb_All";
            var policy = new CacheItemPolicy {
                AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(300)
            };
            List<ClsTb_Bo> data = mc.GetFromCacheOrFetch(
                key,
                _syncRoot_ClsTb_All,
                policy,
                () => new ClsTb_Bll().SltAll(idOfPrjDb)
            );
            return data;
        }

        #endregion ClsTb_All.

        #region PropCol_All:

        private static readonly object _syncRoot_PropCol_All = new object();
        public List<PropCol_Bo> PropCol_All(ulong idOfPrjDb)
        {
            var mc = new MemoryCachePlus();
            string key = "PropCol_All";
            var policy = new CacheItemPolicy {
                AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(300)
            };
            List<PropCol_Bo> data = mc.GetFromCacheOrFetch(
                key,
                _syncRoot_PropCol_All,
                policy,
                () => new PropCol_Bll().SltAll(idOfPrjDb)
            );
            return data;
        }

        #endregion PropCol_All.

    }
}

Code Snippet - 上层封装,与具体的缓存实现无关

Source: .NET 缓存模块设计 Tony Praise

public User GetV3(int id)
{
    if(id <= 0) {
        throw new ArgumentNullException("id");
    }
    var key = string.Format(USER_CACHE_KEY, id);
    return _cacheHelperV2.Get<User>(key, () => _repository.Get(id));
}

//ICache Get<T> 实现
public T Get<T>(string key, Func<T> fetch = null)
{
    T result = default(T);
    var obj = Cache.Get(key);
    if(obj is T) {
        result = (T)obj;
    }

    if(result == null) {
        result = fetch();
        if(result != null) {
            Set(key, result);
        }
    }

    return result;
}

Example - from MSDN

MSDN 上 与 MemoryCache 相关的所有文档,有示例代码的仅以下几个页面:

  1. System.Runtime.Caching.MemoryCache 类:
  2. System.Runtime.Caching.CacheItemPolicy:
  3. System.Runtime.Caching.HostFileChangeMonitor
  4. System.Runtime.Caching.FileChangeMonitor
  5. 演练:在 ASP.NET 中缓存应用程序数据

上述所有页面中的示例代码,与以下代码只有非实质性的微小差异,而且,该代码仅涉及非常基本的 API,对读者帮助有限:

private void Button_Click(object sender, EventArgs e)
{
    ObjectCache cache = MemoryCache.Default;
    string fileContents = cache["filecontents"] as string;

    if (fileContents == null) {
        CacheItemPolicy policy = new CacheItemPolicy();
        policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10.0);

        List<string> filePaths = new List<string>();
        filePaths.Add("c:\\cache\\example.txt");

        policy.ChangeMonitors.Add(new HostFileChangeMonitor(filePaths));

        // Fetch the file contents.
        fileContents = File.ReadAllText("c:\\cache\\example.txt");

        cache.Set("filecontents", fileContents, policy);
    }
    Label1.Text = fileContents;
}

CacheItemPolicy 验证 - 源码分析

以下将 Line-218 的 ValidatePolicy() 称为 验证方法

结论

System.Runtime.Caching.MemoryCache 类 中,所有 新增缓存项 的方法 都调用了 验证方法,该方法对 CacheItemPolicy 实参进行验证。

调用路径

  • Line-450 的 Add() (唯一重载) 调用了 Line-469 的 AddOrGetExisting()
  • AddOrGetExisting() 共有 3 个重载,分别在 Line-459、Line-469、Line-477,前两个重载调用了最后一个重载,最后一个重载调用了 Line-274 的 AddOrGetExistingInternal(),该方法调用了 验证方法
  • Set() 共有 3 个重载,分别在 Line-499、Line-509、Line-516,前两个重载调用了最后一个重载,最后一个重载调用了 验证方法

验证方法 的逻辑

private void ValidatePolicy(CacheItemPolicy policy)
{
    if(
        policy.AbsoluteExpiration != ObjectCache.InfiniteAbsoluteExpiration
        &&
        policy.SlidingExpiration != ObjectCache.NoSlidingExpiration
    ){
        throw new ArgumentException(R.Invalid_expiration_combination, "policy");
    }

    if(
        policy.SlidingExpiration < ObjectCache.NoSlidingExpiration
        ||
        policy.SlidingExpiration > MemoryCache.OneYear
    ){
        string paramName = "policy";
        ...
        throw new ArgumentOutOfRangeException(paramName, message);
    }

    if(
        policy.RemovedCallback != null
        &&
        policy.UpdateCallback != null
    ){
        throw new ArgumentException(R.Invalid_callback_combination, "policy");
    }

    if(
        policy.Priority == CacheItemPriority.Default
        ||
        policy.Priority == CacheItemPriority.NotRemovable
    ){
        return;
    }

    string paramName = "policy";
    ...
    throw new ArgumentOutOfRangeException(paramName, message);
}

CacheItemPolicy 验证 - 相关文档

以下内容复制自 System.Runtime.Caching.MemoryCache.Set)。

ArgumentException

An invalid combination of arguments for the cache entry was passed.
传递了 缓存项的 无效参数组合。

This occurs if the following expiration details are set on the policy object for the cache entry:
如果 在缓存项的策略对象上 设置了 下列过期详细信息,则会发生此情况:

- If both the absolute and sliding expiration values of the CacheItemPolicy object are set to values other than the defaults of InfiniteAbsoluteExpiration and NoSlidingExpiration fields.
- 如果 CacheItemPolicy 对象的 绝对过期值 和 滑动过期值 都设置为 InfiniteAbsoluteExpiration 和 NoSlidingExpiration 字段的默认值 以外的值。

The MemoryCache class cannot set expiration policy based on both an absolute expiration and a sliding expiration.
MemoryCache 类 不能同时依据 绝对过期 和 滑动过期 设置 过期策略。

Only one expiration setting can be explicitly set when you use the MemoryCache class.
在使用 MemoryCache 类时,只能 显式设置 一个过期设置。

The other setting must be set to InfiniteAbsoluteExpiration or NoSlidingExpiration property.
另一个设置 必须设置为 InfiniteAbsoluteExpiration 或 NoSlidingExpiration 属性。

If both the removal callback and the update callback are specified for CacheItemPolicy object.
如果为 CacheItemPolicy 对象 同时指定了 删除回调 和 更新回调。

The MemoryCache class only supports using one type of callback per cache entry.
MemoryCache 类只支持 每个缓存条目 使用 一种类型的回调。

ArgumentOutOfRangeException

The SlidingExpiration property is set to a value less than Zero.
SlidingExpiration 属性 被设置为 小于 Zero 的值。

-or- The SlidingExpiration property is set to a value greater than one year.
-或- SlidingExpiration 属性 被设置为 大于一年的值。

-or- The Priority is not a value of the CacheItemPriority enumeration.
-或- 优先级 不是 CacheItemPriority 枚举的值。

Source Code of System.Runtime.Caching.ObjectCache from JetBrains dotPeek 1.0 at 2018-10-07

Complete Copy:

// Type: System.Runtime.Caching.ObjectCache
// Assembly: System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.Caching.dll

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Caching.Resources;
using System.Security;
using System.Security.Permissions;
using System.Threading;

namespace System.Runtime.Caching
{
  public abstract class ObjectCache : IEnumerable<KeyValuePair<string, object>>, IEnumerable
  {
    public static readonly DateTimeOffset InfiniteAbsoluteExpiration = DateTimeOffset.MaxValue;
    public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
    private static IServiceProvider _host;

    public static IServiceProvider Host
    {
      [SecurityCritical, PermissionSet(SecurityAction.Demand, Unrestricted = true)] get
      {
        return ObjectCache._host;
      }
      [SecurityCritical, PermissionSet(SecurityAction.Demand, Unrestricted = true)] set
      {
        if (value == null)
          throw new ArgumentNullException("value");
        if (Interlocked.CompareExchange<IServiceProvider>(ref ObjectCache._host, value, (IServiceProvider) null) != null)
          throw new InvalidOperationException(R.Property_already_set);
      }
    }

    public abstract DefaultCacheCapabilities DefaultCacheCapabilities { get; }

    public abstract string Name { get; }

    public abstract object this[string key] { get; set; }

    static ObjectCache()
    {
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return (IEnumerator) this.GetEnumerator();
    }

    public abstract CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> keys, string regionName = null);

    IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
    {
      return this.GetEnumerator();
    }

    protected abstract IEnumerator<KeyValuePair<string, object>> GetEnumerator();

    public abstract bool Contains(string key, string regionName = null);

    public virtual bool Add(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
    {
      return this.AddOrGetExisting(key, value, absoluteExpiration, regionName) == null;
    }

    public virtual bool Add(CacheItem item, CacheItemPolicy policy)
    {
      return this.AddOrGetExisting(item, policy) == null;
    }

    public virtual bool Add(string key, object value, CacheItemPolicy policy, string regionName = null)
    {
      return this.AddOrGetExisting(key, value, policy, regionName) == null;
    }

    public abstract object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null);

    public abstract CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy);

    public abstract object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null);

    public abstract object Get(string key, string regionName = null);

    public abstract CacheItem GetCacheItem(string key, string regionName = null);

    public abstract void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null);

    public abstract void Set(CacheItem item, CacheItemPolicy policy);

    public abstract void Set(string key, object value, CacheItemPolicy policy, string regionName = null);

    public abstract IDictionary<string, object> GetValues(IEnumerable<string> keys, string regionName = null);

    public virtual IDictionary<string, object> GetValues(string regionName, params string[] keys)
    {
      return this.GetValues((IEnumerable<string>) keys, regionName);
    }

    public abstract object Remove(string key, string regionName = null);

    public abstract long GetCount(string regionName = null);
  }
}

Source Code of System.Runtime.Caching.MemoryCache from JetBrains dotPeek 1.0 at 2018-10-07

Complete Copy:

// Type: System.Runtime.Caching.MemoryCache
// Assembly: System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.Caching.dll

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Runtime.Caching.Resources;
using System.Security;
using System.Security.Permissions;
using System.Threading;

namespace System.Runtime.Caching
{
  public class MemoryCache : ObjectCache, IEnumerable, IDisposable
  {
    private static readonly TimeSpan OneYear = new TimeSpan(365, 0, 0, 0);
    private static object s_initLock = new object();
    private static CacheEntryRemovedCallback s_sentinelRemovedCallback = new CacheEntryRemovedCallback(MemoryCache.SentinelEntry.OnCacheEntryRemovedCallback);
    private const DefaultCacheCapabilities CAPABILITIES = DefaultCacheCapabilities.InMemoryProvider | DefaultCacheCapabilities.CacheEntryChangeMonitors | DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations | DefaultCacheCapabilities.CacheEntryUpdateCallback | DefaultCacheCapabilities.CacheEntryRemovedCallback;
    private static MemoryCache s_defaultCache;
    private MemoryCacheStore[] _stores;
    private int _storeCount;
    private int _storeMask;
    private int _disposed;
    private MemoryCacheStatistics _stats;
    private string _name;
    private PerfCounters _perfCounters;
    private bool _configLess;
    private EventHandler _onAppDomainUnload;
    private UnhandledExceptionEventHandler _onUnhandledException;

    private bool IsDisposed
    {
      get
      {
        return this._disposed == 1;
      }
    }

    internal bool ConfigLess
    {
      get
      {
        return this._configLess;
      }
    }

    public long CacheMemoryLimit
    {
      get
      {
        return this._stats.CacheMemoryLimit;
      }
    }

    public static MemoryCache Default
    {
      get
      {
        if (MemoryCache.s_defaultCache == null)
        {
          bool lockTaken = false;
          object obj;
          try
          {
            obj = MemoryCache.s_initLock;
            Monitor.Enter(obj, ref lockTaken);
            if (MemoryCache.s_defaultCache == null)
              MemoryCache.s_defaultCache = new MemoryCache();
          }
          finally
          {
            if (lockTaken)
              Monitor.Exit(obj);
          }
        }
        return MemoryCache.s_defaultCache;
      }
    }

    public override DefaultCacheCapabilities DefaultCacheCapabilities
    {
      get
      {
        return DefaultCacheCapabilities.InMemoryProvider | DefaultCacheCapabilities.CacheEntryChangeMonitors | DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations | DefaultCacheCapabilities.CacheEntryUpdateCallback | DefaultCacheCapabilities.CacheEntryRemovedCallback;
      }
    }

    public override string Name
    {
      get
      {
        return this._name;
      }
    }

    public long PhysicalMemoryLimit
    {
      get
      {
        return this._stats.PhysicalMemoryLimit;
      }
    }

    public TimeSpan PollingInterval
    {
      get
      {
        return this._stats.PollingInterval;
      }
    }

    public override object this[string key]
    {
      get
      {
        return this.GetInternal(key, (string) null);
      }
      set
      {
        base.Set(key, value, ObjectCache.InfiniteAbsoluteExpiration, (string) null);
      }
    }

    static MemoryCache()
    {
    }

    private MemoryCache()
    {
      this._name = "Default";
      this.Init((NameValueCollection) null);
    }

    public MemoryCache(string name, NameValueCollection config = null)
    {
      if (name == null)
        throw new ArgumentNullException("name");
      if (name == string.Empty)
        throw new ArgumentException(R.Empty_string_invalid, "name");
      if (string.Equals(name, "default", StringComparison.OrdinalIgnoreCase))
        throw new ArgumentException(R.Default_is_reserved, "name");
      this._name = name;
      this.Init(config);
    }

    internal MemoryCache(string name, NameValueCollection config, bool configLess)
    {
      if (name == null)
        throw new ArgumentNullException("name");
      if (name == string.Empty)
        throw new ArgumentException(R.Empty_string_invalid, "name");
      if (string.Equals(name, "default", StringComparison.OrdinalIgnoreCase))
        throw new ArgumentException(R.Default_is_reserved, "name");
      this._name = name;
      this._configLess = configLess;
      this.Init(config);
    }

    internal MemoryCacheStore GetStore(MemoryCacheKey cacheKey)
    {
      int num = cacheKey.Hash;
      if (num < 0)
        num = num == int.MinValue ? 0 : -num;
      return this._stores[num & this._storeMask];
    }

    [SecuritySafeCritical]
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    private void InitDisposableMembers(NameValueCollection config)
    {
      bool flag = true;
      try
      {
        try
        {
          this._perfCounters = new PerfCounters(this._name);
        }
        catch
        {
        }
        for (int index = 0; index < this._stores.Length; ++index)
          this._stores[index] = new MemoryCacheStore(this, this._perfCounters);
        this._stats = new MemoryCacheStatistics(this, config);
        AppDomain domain = Thread.GetDomain();
        EventHandler eventHandler1 = new EventHandler(this.OnAppDomainUnload);
        EventHandler eventHandler2 = eventHandler1;
        domain.DomainUnload += eventHandler2;
        this._onAppDomainUnload = eventHandler1;
        UnhandledExceptionEventHandler exceptionEventHandler1 = new UnhandledExceptionEventHandler(this.OnUnhandledException);
        UnhandledExceptionEventHandler exceptionEventHandler2 = exceptionEventHandler1;
        domain.UnhandledException += exceptionEventHandler2;
        this._onUnhandledException = exceptionEventHandler1;
        flag = false;
      }
      finally
      {
        if (flag)
          this.Dispose();
      }
    }

    private void OnAppDomainUnload(object unusedObject, EventArgs unusedEventArgs)
    {
      this.Dispose();
    }

    private void OnUnhandledException(object sender, UnhandledExceptionEventArgs eventArgs)
    {
      if (!eventArgs.IsTerminating)
        return;
      this.Dispose();
    }

    private void ValidatePolicy(CacheItemPolicy policy)
    {
      if (policy.AbsoluteExpiration != ObjectCache.InfiniteAbsoluteExpiration && policy.SlidingExpiration != ObjectCache.NoSlidingExpiration)
        throw new ArgumentException(R.Invalid_expiration_combination, "policy");
      if (policy.SlidingExpiration < ObjectCache.NoSlidingExpiration || MemoryCache.OneYear < policy.SlidingExpiration)
      {
        string paramName = "policy";
        string argumentOutOfRange = R.Argument_out_of_range;
        object[] objArray = new object[3];
        int index1 = 0;
        string str = "SlidingExpiration";
        objArray[index1] = (object) str;
        int index2 = 1;
        // ISSUE: variable of a boxed type
        __Boxed<TimeSpan> local1 = (ValueType) ObjectCache.NoSlidingExpiration;
        objArray[index2] = (object) local1;
        int index3 = 2;
        // ISSUE: variable of a boxed type
        __Boxed<TimeSpan> local2 = (ValueType) MemoryCache.OneYear;
        objArray[index3] = (object) local2;
        string message = RH.Format(argumentOutOfRange, objArray);
        throw new ArgumentOutOfRangeException(paramName, message);
      }
      else
      {
        if (policy.RemovedCallback != null && policy.UpdateCallback != null)
          throw new ArgumentException(R.Invalid_callback_combination, "policy");
        if (policy.Priority == CacheItemPriority.Default || policy.Priority == CacheItemPriority.NotRemovable)
          return;
        string paramName = "policy";
        string argumentOutOfRange = R.Argument_out_of_range;
        object[] objArray = new object[3];
        int index1 = 0;
        string str = "Priority";
        objArray[index1] = (object) str;
        int index2 = 1;
        // ISSUE: variable of a boxed type
        __Boxed<CacheItemPriority> local1 = (Enum) CacheItemPriority.Default;
        objArray[index2] = (object) local1;
        int index3 = 2;
        // ISSUE: variable of a boxed type
        __Boxed<CacheItemPriority> local2 = (Enum) CacheItemPriority.NotRemovable;
        objArray[index3] = (object) local2;
        string message = RH.Format(argumentOutOfRange, objArray);
        throw new ArgumentOutOfRangeException(paramName, message);
      }
    }

    private void Init(NameValueCollection config)
    {
      this._storeCount = Environment.ProcessorCount;
      this._storeMask = this._storeCount - 1;
      this._stores = new MemoryCacheStore[this._storeCount];
      this.InitDisposableMembers(config);
    }

    private object AddOrGetExistingInternal(string key, object value, CacheItemPolicy policy)
    {
      if (key == null)
        throw new ArgumentNullException("key");
      DateTimeOffset absExp = ObjectCache.InfiniteAbsoluteExpiration;
      TimeSpan slidingExp = ObjectCache.NoSlidingExpiration;
      CacheItemPriority priority = CacheItemPriority.Default;
      Collection<ChangeMonitor> dependencies = (Collection<ChangeMonitor>) null;
      CacheEntryRemovedCallback removedCallback = (CacheEntryRemovedCallback) null;
      if (policy != null)
      {
        this.ValidatePolicy(policy);
        if (policy.UpdateCallback != null)
          throw new ArgumentException(R.Update_callback_must_be_null, "policy");
        absExp = policy.AbsoluteExpiration;
        slidingExp = policy.SlidingExpiration;
        priority = policy.Priority;
        dependencies = policy.ChangeMonitors;
        removedCallback = policy.RemovedCallback;
      }
      if (this.IsDisposed)
      {
        if (dependencies != null)
        {
          foreach (ChangeMonitor changeMonitor in dependencies)
          {
            if (changeMonitor != null)
              changeMonitor.Dispose();
          }
        }
        return (object) null;
      }
      else
      {
        MemoryCacheKey memoryCacheKey = new MemoryCacheKey(key);
        MemoryCacheEntry existing = this.GetStore(memoryCacheKey).AddOrGetExisting(memoryCacheKey, new MemoryCacheEntry(key, value, absExp, slidingExp, priority, dependencies, removedCallback, this));
        if (existing == null)
          return (object) null;
        else
          return existing.Value;
      }
    }

    public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> keys, string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      if (keys == null)
        throw new ArgumentNullException("keys");
      List<string> list = new List<string>(keys);
      if (list.Count == 0)
      {
        string emptyCollection = R.Empty_collection;
        object[] objArray = new object[1];
        int index = 0;
        string str = "keys";
        objArray[index] = (object) str;
        throw new ArgumentException(RH.Format(emptyCollection, objArray));
      }
      else
      {
        foreach (string str1 in list)
        {
          if (str1 == null)
          {
            string containsNullElement = R.Collection_contains_null_element;
            object[] objArray = new object[1];
            int index = 0;
            string str2 = "keys";
            objArray[index] = (object) str2;
            throw new ArgumentException(RH.Format(containsNullElement, objArray));
          }
        }
        return (CacheEntryChangeMonitor) new MemoryCacheEntryChangeMonitor(list.AsReadOnly(), regionName, this);
      }
    }

    public void Dispose()
    {
      if (Interlocked.Exchange(ref this._disposed, 1) != 0)
        return;
      this.DisposeSafeCritical();
      if (this._stats != null)
        this._stats.Dispose();
      if (this._stores != null)
      {
        foreach (MemoryCacheStore memoryCacheStore in this._stores)
        {
          if (memoryCacheStore != null)
            memoryCacheStore.Dispose();
        }
      }
      if (this._perfCounters != null)
        this._perfCounters.Dispose();
      GC.SuppressFinalize((object) this);
    }

    [SecuritySafeCritical]
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    private void DisposeSafeCritical()
    {
      AppDomain domain = Thread.GetDomain();
      if (this._onAppDomainUnload != null)
        domain.DomainUnload -= this._onAppDomainUnload;
      if (this._onUnhandledException == null)
        return;
      domain.UnhandledException -= this._onUnhandledException;
    }

    private object GetInternal(string key, string regionName)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      if (key == null)
        throw new ArgumentNullException("key");
      MemoryCacheEntry entry = this.GetEntry(key);
      if (entry == null)
        return (object) null;
      else
        return entry.Value;
    }

    internal MemoryCacheEntry GetEntry(string key)
    {
      if (this.IsDisposed)
        return (MemoryCacheEntry) null;
      MemoryCacheKey memoryCacheKey = new MemoryCacheKey(key);
      return this.GetStore(memoryCacheKey).Get(memoryCacheKey);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      Hashtable hashtable = new Hashtable();
      if (!this.IsDisposed)
      {
        foreach (MemoryCacheStore memoryCacheStore in this._stores)
          memoryCacheStore.CopyTo((IDictionary) hashtable);
      }
      return (IEnumerator) hashtable.GetEnumerator();
    }

    protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator()
    {
      Dictionary<string, object> dictionary = new Dictionary<string, object>();
      if (!this.IsDisposed)
      {
        foreach (MemoryCacheStore memoryCacheStore in this._stores)
          memoryCacheStore.CopyTo((IDictionary) dictionary);
      }
      return (IEnumerator<KeyValuePair<string, object>>) dictionary.GetEnumerator();
    }

    internal MemoryCacheEntry RemoveEntry(string key, MemoryCacheEntry entry, CacheEntryRemovedReason reason)
    {
      MemoryCacheKey memoryCacheKey = new MemoryCacheKey(key);
      return this.GetStore(memoryCacheKey).Remove(memoryCacheKey, entry, reason);
    }

    public long Trim(int percent)
    {
      if (percent > 100)
        percent = 100;
      long num = 0L;
      if (this._disposed == 0)
      {
        foreach (MemoryCacheStore memoryCacheStore in this._stores)
          num += memoryCacheStore.TrimInternal(percent);
      }
      return num;
    }

    public override bool Contains(string key, string regionName = null)
    {
      return this.GetInternal(key, regionName) != null;
    }

    public override bool Add(CacheItem item, CacheItemPolicy policy)
    {
      CacheItem existing = this.AddOrGetExisting(item, policy);
      if (existing != null)
        return existing.Value == null;
      else
        return true;
    }

    public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      return this.AddOrGetExistingInternal(key, value, new CacheItemPolicy()
      {
        AbsoluteExpiration = absoluteExpiration
      });
    }

    public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
    {
      if (item == null)
        throw new ArgumentNullException("item");
      else
        return new CacheItem(item.Key, this.AddOrGetExistingInternal(item.Key, item.Value, policy));
    }

    public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      else
        return this.AddOrGetExistingInternal(key, value, policy);
    }

    public override object Get(string key, string regionName = null)
    {
      return this.GetInternal(key, regionName);
    }

    public override CacheItem GetCacheItem(string key, string regionName = null)
    {
      object @internal = this.GetInternal(key, regionName);
      if (@internal == null)
        return (CacheItem) null;
      else
        return new CacheItem(key, @internal);
    }

    public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      base.Set(key, value, new CacheItemPolicy()
      {
        AbsoluteExpiration = absoluteExpiration
      }, (string) null);
    }

    public override void Set(CacheItem item, CacheItemPolicy policy)
    {
      if (item == null)
        throw new ArgumentNullException("item");
      base.Set(item.Key, item.Value, policy, (string) null);
    }

    public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      if (key == null)
        throw new ArgumentNullException("key");
      DateTimeOffset absExp = ObjectCache.InfiniteAbsoluteExpiration;
      TimeSpan slidingExp = ObjectCache.NoSlidingExpiration;
      CacheItemPriority priority = CacheItemPriority.Default;
      Collection<ChangeMonitor> dependencies = (Collection<ChangeMonitor>) null;
      CacheEntryRemovedCallback removedCallback = (CacheEntryRemovedCallback) null;
      if (policy != null)
      {
        this.ValidatePolicy(policy);
        if (policy.UpdateCallback != null)
        {
          this.Set(key, value, policy.ChangeMonitors, policy.AbsoluteExpiration, policy.SlidingExpiration, policy.UpdateCallback);
          return;
        }
        else
        {
          absExp = policy.AbsoluteExpiration;
          slidingExp = policy.SlidingExpiration;
          priority = policy.Priority;
          dependencies = policy.ChangeMonitors;
          removedCallback = policy.RemovedCallback;
        }
      }
      if (this.IsDisposed)
      {
        if (dependencies == null)
          return;
        foreach (ChangeMonitor changeMonitor in dependencies)
        {
          if (changeMonitor != null)
            changeMonitor.Dispose();
        }
      }
      else
      {
        MemoryCacheKey memoryCacheKey = new MemoryCacheKey(key);
        this.GetStore(memoryCacheKey).Set(memoryCacheKey, new MemoryCacheEntry(key, value, absExp, slidingExp, priority, dependencies, removedCallback, this));
      }
    }

    internal void Set(string key, object value, Collection<ChangeMonitor> changeMonitors, DateTimeOffset absoluteExpiration, TimeSpan slidingExpiration, CacheEntryUpdateCallback onUpdateCallback)
    {
      if (key == null)
        throw new ArgumentNullException("key");
      if (changeMonitors == null && absoluteExpiration == ObjectCache.InfiniteAbsoluteExpiration && slidingExpiration == ObjectCache.NoSlidingExpiration)
        throw new ArgumentException(R.Invalid_argument_combination);
      if (onUpdateCallback == null)
        throw new ArgumentNullException("onUpdateCallback");
      if (this.IsDisposed)
      {
        if (changeMonitors == null)
          return;
        foreach (ChangeMonitor changeMonitor in changeMonitors)
        {
          if (changeMonitor != null)
            changeMonitor.Dispose();
        }
      }
      else
      {
        MemoryCacheKey cacheKey = new MemoryCacheKey(key);
        MemoryCacheStore store1 = this.GetStore(cacheKey);
        MemoryCacheEntry memoryCacheEntry1 = new MemoryCacheEntry(key, value, ObjectCache.InfiniteAbsoluteExpiration, ObjectCache.NoSlidingExpiration, CacheItemPriority.NotRemovable, (Collection<ChangeMonitor>) null, (CacheEntryRemovedCallback) null, this);
        MemoryCacheKey key1 = cacheKey;
        MemoryCacheEntry entry = memoryCacheEntry1;
        store1.Set(key1, entry);
        string[] strArray = new string[1];
        int index = 0;
        string str = key;
        strArray[index] = str;
        ChangeMonitor expensiveObjectDependency = (ChangeMonitor) this.CreateCacheEntryChangeMonitor((IEnumerable<string>) strArray, (string) null);
        if (changeMonitors == null)
          changeMonitors = new Collection<ChangeMonitor>();
        changeMonitors.Add(expensiveObjectDependency);
        MemoryCacheKey memoryCacheKey = new MemoryCacheKey("OnUpdateSentinel" + key);
        MemoryCacheStore store2 = this.GetStore(memoryCacheKey);
        MemoryCacheEntry memoryCacheEntry2 = new MemoryCacheEntry(memoryCacheKey.Key, (object) new MemoryCache.SentinelEntry(key, expensiveObjectDependency, onUpdateCallback), absoluteExpiration, slidingExpiration, CacheItemPriority.NotRemovable, changeMonitors, MemoryCache.s_sentinelRemovedCallback, this);
        store2.Set(memoryCacheKey, memoryCacheEntry2);
        memoryCacheEntry1.ConfigureUpdateSentinel(store2, memoryCacheEntry2);
      }
    }

    public override object Remove(string key, string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      if (key == null)
        throw new ArgumentNullException("key");
      if (this.IsDisposed)
        return (object) null;
      MemoryCacheEntry memoryCacheEntry = this.RemoveEntry(key, (MemoryCacheEntry) null, CacheEntryRemovedReason.Removed);
      if (memoryCacheEntry == null)
        return (object) null;
      else
        return memoryCacheEntry.Value;
    }

    public override long GetCount(string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      long num = 0L;
      if (!this.IsDisposed)
      {
        foreach (MemoryCacheStore memoryCacheStore in this._stores)
          num += (long) memoryCacheStore.Count;
      }
      return num;
    }

    public override IDictionary<string, object> GetValues(IEnumerable<string> keys, string regionName = null)
    {
      if (regionName != null)
        throw new NotSupportedException(R.RegionName_not_supported);
      if (keys == null)
        throw new ArgumentNullException("keys");
      Dictionary<string, object> dictionary = (Dictionary<string, object>) null;
      if (!this.IsDisposed)
      {
        foreach (string key in keys)
        {
          if (key == null)
          {
            string containsNullElement = R.Collection_contains_null_element;
            object[] objArray = new object[1];
            int index = 0;
            string str = "keys";
            objArray[index] = (object) str;
            throw new ArgumentException(RH.Format(containsNullElement, objArray));
          }
          else
          {
            object @internal = this.GetInternal(key, (string) null);
            if (@internal != null)
            {
              if (dictionary == null)
                dictionary = new Dictionary<string, object>();
              dictionary[key] = @internal;
            }
          }
        }
      }
      return (IDictionary<string, object>) dictionary;
    }

    internal void UpdateConfig(NameValueCollection config)
    {
      if (config == null)
        throw new ArgumentNullException("config");
      if (this.IsDisposed)
        return;
      this._stats.UpdateConfig(config);
    }

    private class SentinelEntry
    {
      private string _key;
      private ChangeMonitor _expensiveObjectDependency;
      private CacheEntryUpdateCallback _updateCallback;

      internal string Key
      {
        get
        {
          return this._key;
        }
      }

      internal ChangeMonitor ExpensiveObjectDependency
      {
        get
        {
          return this._expensiveObjectDependency;
        }
      }

      internal CacheEntryUpdateCallback CacheEntryUpdateCallback
      {
        get
        {
          return this._updateCallback;
        }
      }

      internal SentinelEntry(string key, ChangeMonitor expensiveObjectDependency, CacheEntryUpdateCallback callback)
      {
        this._key = key;
        this._expensiveObjectDependency = expensiveObjectDependency;
        this._updateCallback = callback;
      }

      private static bool IsPolicyValid(CacheItemPolicy policy)
      {
        if (policy == null)
          return false;
        bool flag = false;
        Collection<ChangeMonitor> changeMonitors = policy.ChangeMonitors;
        if (changeMonitors != null)
        {
          foreach (ChangeMonitor changeMonitor in changeMonitors)
          {
            if (changeMonitor != null && changeMonitor.HasChanged)
            {
              flag = true;
              break;
            }
          }
        }
        if (!flag && policy.UpdateCallback != null)
          return true;
        if (flag)
        {
          foreach (ChangeMonitor changeMonitor in changeMonitors)
          {
            if (changeMonitor != null)
              changeMonitor.Dispose();
          }
        }
        return false;
      }

      internal static void OnCacheEntryRemovedCallback(CacheEntryRemovedArguments arguments)
      {
        MemoryCache memoryCache = arguments.Source as MemoryCache;
        MemoryCache.SentinelEntry sentinelEntry = arguments.CacheItem.Value as MemoryCache.SentinelEntry;
        CacheEntryRemovedReason removedReason = arguments.RemovedReason;
        switch (removedReason)
        {
          case CacheEntryRemovedReason.Expired:
            try
            {
              CacheEntryUpdateArguments arguments1 = new CacheEntryUpdateArguments((ObjectCache) memoryCache, removedReason, sentinelEntry.Key, (string) null);
              sentinelEntry.CacheEntryUpdateCallback(arguments1);
              object obj = arguments1.UpdatedCacheItem != null ? arguments1.UpdatedCacheItem.Value : (object) null;
              CacheItemPolicy updatedCacheItemPolicy = arguments1.UpdatedCacheItemPolicy;
              if (obj != null && MemoryCache.SentinelEntry.IsPolicyValid(updatedCacheItemPolicy))
              {
                ((ObjectCache) memoryCache).Set(sentinelEntry.Key, obj, updatedCacheItemPolicy, (string) null);
                break;
              }
              else
              {
                memoryCache.Remove(sentinelEntry.Key, (string) null);
                break;
              }
            }
            catch
            {
              memoryCache.Remove(sentinelEntry.Key, (string) null);
              break;
            }
          case CacheEntryRemovedReason.ChangeMonitorChanged:
            if (sentinelEntry.ExpensiveObjectDependency.HasChanged)
              break;
            else
              goto case CacheEntryRemovedReason.Expired;
        }
      }
    }
  }
}

Source Code of System.Runtime.Caching.CacheItemPolicy from JetBrains dotPeek 1.0 at 2018-10-08

Complete Copy:

// Type: System.Runtime.Caching.CacheItemPolicy
// Assembly: System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.Caching.dll

using System;
using System.Collections.ObjectModel;

namespace System.Runtime.Caching
{
  public class CacheItemPolicy
  {
    private DateTimeOffset _absExpiry;
    private TimeSpan _sldExpiry;
    private Collection<ChangeMonitor> _changeMonitors;
    private CacheItemPriority _priority;
    private CacheEntryRemovedCallback _removedCallback;
    private CacheEntryUpdateCallback _updateCallback;

    public DateTimeOffset AbsoluteExpiration
    {
      get
      {
        return this._absExpiry;
      }
      set
      {
        this._absExpiry = value;
      }
    }

    public Collection<ChangeMonitor> ChangeMonitors
    {
      get
      {
        if (this._changeMonitors == null)
          this._changeMonitors = new Collection<ChangeMonitor>();
        return this._changeMonitors;
      }
    }

    public CacheItemPriority Priority
    {
      get
      {
        return this._priority;
      }
      set
      {
        this._priority = value;
      }
    }

    public CacheEntryRemovedCallback RemovedCallback
    {
      get
      {
        return this._removedCallback;
      }
      set
      {
        this._removedCallback = value;
      }
    }

    public TimeSpan SlidingExpiration
    {
      get
      {
        return this._sldExpiry;
      }
      set
      {
        this._sldExpiry = value;
      }
    }

    public CacheEntryUpdateCallback UpdateCallback
    {
      get
      {
        return this._updateCallback;
      }
      set
      {
        this._updateCallback = value;
      }
    }

    public CacheItemPolicy()
    {
      this._absExpiry = ObjectCache.InfiniteAbsoluteExpiration;
      this._sldExpiry = ObjectCache.NoSlidingExpiration;
      this._priority = CacheItemPriority.Default;
    }
  }
}

Source Code of System.Runtime.Caching.CacheItemPriority from JetBrains dotPeek 1.0 at 2018-10-08

Complete Copy:

// Type: System.Runtime.Caching.CacheItemPriority
// Assembly: System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.Caching.dll

namespace System.Runtime.Caching
{
  public enum CacheItemPriority
  {
    Default,
    NotRemovable,
  }
}