| Severity | Error |
| Reported by | PostSharp.Patterns.Caching |
| Message | Cannot add the [Cache] aspect to the <Method> method because it has a ref or out parameter. |
Cause
The CacheAttribute aspect stores the return value of a method, under a key built from its arguments. A method with a ref or out parameter also returns a value through this parameter, which the cache cannot store. PostSharp therefore reports CAC010 for a method with a ref or out parameter. An in parameter is accepted, because the method cannot assign it.
In the following example, the TryGetPrice method returns the price in an out parameter.
using System;
using System.Collections.Generic;
using PostSharp.Patterns.Caching;
using PostSharp.Patterns.Caching.Backends;
namespace Errors.CAC010_Error;
public class PriceService
{
private readonly Dictionary<string, decimal> prices = new() { ["P-100"] = 9.90m };
// The method returns the price in an out parameter, which the cache cannot store.
[Cache]
public bool TryGetPrice( string productId, out decimal price )
{
Console.WriteLine( $"Loading the price of {productId} from the database." );
return this.prices.TryGetValue( productId, out price );
}
}
public static class Program
{
public static void Main()
{
CachingServices.DefaultService.Backend = new MemoryCachingBackend();
var service = new PriceService();
for ( var i = 0; i < 2; i++ )
{
if ( service.TryGetPrice( "P-100", out var price ) )
{
Console.WriteLine( $"P-100 costs {price}." );
}
}
}
}
error CAC010: CAC010_Error.cs(14,17): Cannot add the [Cache] aspect to the Errors.CAC010_Error.PriceService.TryGetPrice(System.String, System.Decimal@) method because it has a ref or out parameter.
Resolution
Return all the results of the method in its return value: a nullable value instead of the Try pattern, or a tuple or a record when the method has several results. Then apply the CacheAttribute aspect to this method. When you cannot change the method, cache another method that returns the value, and call it from the method that has the ref or out parameter.
In the following example, GetPrice returns null when the product has no price, and the second call is served from the cache.
using System;
using System.Collections.Generic;
using PostSharp.Patterns.Caching;
using PostSharp.Patterns.Caching.Backends;
namespace Errors.CAC010_Fixed;
public class PriceService
{
private readonly Dictionary<string, decimal> prices = new() { ["P-100"] = 9.90m };
// The method returns the price, or null when the product has none, so the cache stores the whole result.
[Cache]
public decimal? GetPrice( string productId )
{
Console.WriteLine( $"Loading the price of {productId} from the database." );
return this.prices.TryGetValue( productId, out var price ) ? price : null;
}
}
public static class Program
{
public static void Main()
{
CachingServices.DefaultService.Backend = new MemoryCachingBackend();
var service = new PriceService();
for ( var i = 0; i < 2; i++ )
{
if ( service.GetPrice( "P-100" ) is { } price )
{
Console.WriteLine( $"P-100 costs {price}." );
}
}
}
}
Loading the price of P-100 from the database.
P-100 costs 9.90.
P-100 costs 9.90.
See Also
Reference
Other Resources