2016-08-16 67 views
1

想象下面的类用作某些复杂请求的参数对象。使用可变对象作为散列键

public class RequestParameters 
{ 
    ///<summary>Detailed User-Friendly Descriptions.</summary> 
    string Param1 { get; set; } = "Default"; 
    int? Param2 { get; set; } 
    bool Param3 { get; set; } = true; 
    ... 
    string ParamN { get; set; } 
} 

我希望用户能够创建并交给它关闭在请求中使用之前配置此参数对象,因为他们认为合适的:

RequestParameters _filtered = new RequestParameters(); 
filtered.Param1 = "yellow"; 
filtered.Param3 = true; 
_someInstance.InvokeRequest(_filtered); 

一旦我处理请求,我希望根据提供的请求参数缓存结果。这将是微不足道的实施一些克隆方法和IEquatable<RequestParameters>,并覆盖上述RequestParametersEquals()GetHashCode(),但后者被广泛认为是不好的做法 - RequestParameters是可变的,如果一个实例被插入后修改,就可能乱用我的缓存。另一方面,如果我要实现某种类的ReadOnlyRequestParameters这个类的副本,而不是使用setter,那么我觉得这将会导致大量的代码重复(以及维护头痛)。

如果我小心地制作用户发送的实例的副本,并且在插入缓存后不再修改它们,那么将此类用作缓存键会真的很糟吗?如果你是一个使用相同对象的开发者,你会提出什么选择?

+1

如何某种工厂,在那里你收集所有参数,可能改变他们前进的道路上,并返回一个不变的副本,需求?关于“代码重复”和“维护头痛”:如何使用T4来保持工厂的读/写属性和不可变副本的只读属性同步? – Corak

+1

你有三种选择。 (1)靠你的裤子飞行,直接使用物体,并希望没有人改变它。 (2)实现'.Freeze()'方法,以防止对对象的进一步更改,以便您可以依赖'GetHashCode' /'Equals'。 (3)创建你的课程的只读版本。 – Enigmativity

+1

实现GetHashCode()over mutable字段的另一个问题是,它是(public)类的公共接口的一部分。客户可能希望将实例存储在他们自己的字典或HashSet中,并且他们可能会期望参考平等行为,因为该类是可变的。 – Blorgbeard

回答

1

磨砂的建议的精神,一个潜在的解决方案(我可能最终使用)是AsReadOnly()解决方案的妥协(它需要复制类结构和大量的复制/粘贴)。

如果此类的主要目标是用户友好的参数化,并且我们可以将其用作缓存键作为二等公民,则实现它的相对简单的方法是创建一个GetHashKey()方法。所有这个方法需要做的是返回一个我们可以调用“Equals”和“GetHashCode”的不可变对象 - 并且保证这个对象的等价实例将被视为相等。元组这样做,但这样做AnonymousTypes,这是更精简,更容易创造参与超过6个参数:

/// <summary>Get a hashable/comparable key representing the current parameters.</summary> 
/// <returns>The hash key.</returns> 
public virtual object GetHashKey() 
{ 
    return new { Param1, Param2, Param3, Param4, ..., ParamN }; 
} 

返回AnonymousType正确实现

  • GetHashCode()为每个值的组合哈希在对象中。
  • Equals()作为对象中每个值的成员方式比较。

使用这个,我们基本上产生了一个RequestParameters对象在其当前状态的快照(复制)。我们牺牲了轻松反映属性的能力,但可以轻松安全地将它们用作Equatable/Hashable缓存键,而无需任何其他类。

在我的具体情况,避免额外的类是特别重要的,因为我不只有1个参数类,我有几十个。更重要的是,许多RequestParameter类彼此继承(因为许多请求共享相同的基本参数)。创建这些类的“ReadOnly”版本的整套将是艰巨的。在此方案中,每个只具有覆盖GetHashKey()配对其基类AnonymousType有自己的附加参数:

public class AdditionalParameters : RequestParameters 
{ 
    ///<summary>Detailed User-Friendly Descriptions.</summary> 
    int? Param42 { get; set; }; 
    ... 
    string Param70 { get; set; } 

    public override object GetHashKey() 
    { 
     return new { base.GetHashKey(), Param42, ..., Param70 }; 
    } 
} 
-1

你总是可以在参数存储在一个元组一旦你收集它们看作是不可变的,一旦创建....

+0

嗨,感谢您的建议(我upvoted反击downvote别人给了。)所以你想像一个GetHashKey()方法,返回元组?这不是一个坏主意。我认为这个建议的问题是我有几十个参数,所以创建一个N-Tuple(在第6个参数后需要嵌套元组)会变得很难看,更不用说我会失去工作的良好面向对象与具有命名属性的类。总而言之,不是一个可怕的想法。 – Alain