最近我发现自己编写了连续调用其他方法的方法,并根据先返回适当值的方法设置了一些值。我一直在做的是用一种方法设置值,然后检查值,如果它不好,那么我检查下一个。这是最近的一个例子:
private void InitContent()
{
if (!String.IsNullOrEmpty(Request.QueryString["id"]))
{
Content = GetContent(Convert.ToInt64(Request.QueryString["id"]));
ContentMode = ContentFrom.Query;
}
if (Content == null && DefaultId != null)
{
Content = GetContent(DefaultId);
ContentMode = ContentFrom.Default;
}
if (Content == null) ContentMode = ContentFrom.None;
}
如果GetContent
不在数据库中,null
方法应返回id
。这是一个简短的例子,但你可以想象如果有更多的选择,这可能会变得笨重。有更好的方法吗?
答案 0 :(得分:9)
null合并运算符可能具有您想要的语义。
q = W() ?? X() ?? Y() ?? Z();
这基本上与:
相同if ((temp = W()) == null && (temp = X()) == null && (temp == Y()) == null)
temp = Z();
q = temp;
也就是说,q是W(),X(),Y()的第一个非null,或者如果所有这些都为null,那么Z()。
你可以随意链接。
确切的语义并不像我描述的那样;类型转换规则很棘手。如果您需要确切的详细信息,请参阅规范。
答案 1 :(得分:3)
你也可以做一些鬼鬼祟祟的事情:
private Int64? GetContentIdOrNull(string id)
{
return string.IsNullOrEmpty(id) ? null : (Int64?)Convert.ToInt64(id);
}
private Int64? GetContentIdOrNull(DefaultIdType id)
{
return id;
}
private void InitContent()
{
// Attempt to get content from multiple sources in order of preference
var contentSources = new Dictionary<ContentFrom, Func<Int64?>> {
{ ContentFrom.Query, () => GetContentIdOrNull(Request.QueryString["id"]) },
{ ContentFrom.Default, () => GetContentIdOrNull(DefaultId) }
};
foreach (var source in contentSources) {
var id = source.Value();
if (!id.HasValue) {
continue;
}
Content = GetContent(id.Value);
ContentMode = source.Key;
if (Content != null) {
return;
}
}
// Default
ContentMode = ContentFrom.None;
}
如果你有更多的资源,那会有所帮助,但会增加复杂性。
答案 2 :(得分:2)
就个人而言,我发现当我有很多看似完全不同的陈述时,是时候做出一些功能了。
private ContentMode GetContentMode(){
}
private Content GetContent(int id){
}
private Content GetContent(HttpRequest request){
return GetContent(Convert.ToInt64(request.QueryString["id"]));
}
private void InitContent(){
ContentMode mode = GetContentMode();
Content = null;
switch(mode){
case ContentMode.Query:
GetContent(Request);
break;
case ContentMode.Default:
GetContent(DefaultId);
break;
case ContentMode.None:
... handle none case...
break;
}
}
这样,你分开你的意图 - 第一步,确定内容模式。然后,获取内容。
答案 3 :(得分:1)
我建议你尝试一下这种情况下的工厂设计模式。您可以通过注册不同的创建者来抽象内容创建过程。此外,您可以为每个创建者添加对您自己的逻辑的偏好。此外,我建议你封装所有与内容相关的数据,就像其他帖子中的“ContentDefinition”类一样。
一般而言,您需要知道在灵活性和效率之间始终存在权衡。有时你的第一个解决方案足够好了:)
答案 4 :(得分:1)
好的,因为我注意到你实际上也想要ContentFrom模式有点迟了,我已经尽力在你的原始答案之下提出你的样本翻译
一般来说,我对这种情况使用以下范例。在这里和那里搜索并替换您的特定方法:)
IEnumerable<T> ValueSources()
{
yield return _value?? _alternative;
yield return SimpleCalculationFromCache();
yield return ComplexCalculation();
yield return PromptUIInputFallback("Please help by entering a value for X:");
}
T EffectiveValue { get { return ValueSources().FirstOrDefault(v => v!=null); } }
请注意您现在可以为您的目的使v!=null
任意'有趣'。
另请注意,当_value或_alternative设置为“有趣”值时,延迟评估如何确保计算永远不会完成
这是我最初尝试将样品放入此模具的过程。请注意我是如何添加相当多的管道来确保它实际编译成独立的C#exe:
using System.Collections.Generic;
using System.Linq;
using System;
using T=System.String;
namespace X { public class Y
{
public static void Main(string[]args)
{
var content = Sources().FirstOrDefault(c => c); // trick: uses operator bool()
}
internal protected struct Content
{
public T Value;
public ContentFrom Mode;
//
public static implicit operator bool(Content specimen) { return specimen.Mode!=ContentFrom.None && null!=specimen.Value; }
}
private static IEnumerable<Content> Sources()
{
// mock
var Request = new { QueryString = new [] {"id"}.ToDictionary(a => a) };
if (!String.IsNullOrEmpty(Request.QueryString["id"]))
yield return new Content { Value = GetContent(Convert.ToInt64(Request.QueryString["id"])), Mode = ContentFrom.Query };
if (DefaultId != null)
yield return new Content { Value = GetContent((long) DefaultId), Mode = ContentFrom.Default };
yield return new Content();
}
public enum ContentFrom { None, Query, Default };
internal static T GetContent(long id) { return "dummy"; }
internal static readonly long? DefaultId = 42;
} }
答案 5 :(得分:0)
private void InitContent()
{
Int64? id = !String.IsNullOrEmpty(Request.QueryString["id"])
? Convert.ToInt64(Request.QueryString["id"])
: null;
if (id != null && (Content = GetContent(id)) != null)
ContentMode = ContentFrom.Query;
else if(DefaultId != null && (Content = GetContent(DefaultId)) != null)
ContentMode = ContentFrom.Default;
else
ContentMode = ContentFrom.None;
}