返回第一种有效的方法,更优雅的方式?

时间:2011-03-22 23:23:14

标签: c# design-patterns

最近我发现自己编写了连续调用其他方法的方法,并根据先返回适当值的方法设置了一些值。我一直在做的是用一种方法设置值,然后检查值,如果它不好,那么我检查下一个。这是最近的一个例子:

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。这是一个简短的例子,但你可以想象如果有更多的选择,这可能会变得笨重。有更好的方法吗?

6 个答案:

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