返回第一个有效的方法,更优雅的方法吗?

发布于 2024-10-25 02:49:21 字数 678 浏览 2 评论 0原文

最近,我发现自己编写的方法连续调用其他方法,并根据哪个方法首先返回适当的值来设置一些值。我一直在做的是用一种方法设置值,然后检查该值,如果不好,则检查下一个方法。下面是一个最近的示例:

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

如果 id 不在数据库中,则 GetContent 方法应返回 null。这是一个简短的示例,但您可以想象如果有更多选项,这可能会变得多么笨拙。有更好的方法吗?

Recently I've found myself writing methods which call other methods in succession and setting some value based on whichever method returns an appropriate value first. What I've been doing is setting the value with one method, then checking the value and if it's not good then I check the next one. Here's a recent example:

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

Here the GetContent method should be returning null if the id isn't in the database. This is a short example, but you can imagine how this might get clunky if there were more options. Is there a better way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

七秒鱼° 2024-11-01 02:49:21

空合并运算符可能具有您想要的语义。

q = W() ?? X() ?? Y() ?? Z();

这本质上与以下相同:

if ((temp = W()) == null && (temp = X()) == null && (temp == Y()) == null)
    temp = Z();
q = temp;

也就是说,q 是 W()、X()、Y() 中的第一个非空,或者如果它们全部为空,则为 Z()。

您可以根据需要链接任意数量的链接。

确切的语义并不完全像我所描绘的那样;类型转换规则很棘手。如果您需要确切的详细信息,请参阅规格。

The null coalescing operator might have the semantics you want.

q = W() ?? X() ?? Y() ?? Z();

That's essentially the same as:

if ((temp = W()) == null && (temp = X()) == null && (temp == Y()) == null)
    temp = Z();
q = temp;

That is, q is the first non-null of W(), X(), Y(), or if all of them are null, then Z().

You can chain as many as you like.

The exact semantics are not quite like I sketched out; the type conversion rules are tricky. See the spec if you need the exact details.

不寐倦长更 2024-11-01 02:49:21

您还可以做一些更偷偷摸摸的事情,大致如下:

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

如果您有更多的来源,这会有所帮助,但代价是增加复杂性。

You could also do something a little more sneaky, along the lines of this:

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

That would help if you had many more sources, at the cost of increased complexity.

风追烟花雨 2024-11-01 02:49:21

就我个人而言,我发现当我有很多看似不同的语句时,就该创建一些函数了。

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;

  }
}

这样,您就可以分离您的意图 - 第一步,确定内容模式。然后,获取内容。

Personally, I find when I have lots of statements that are seemingly disparate, it's time to make some functions.

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;

  }
}

This way, you separate your intentions - first step, determine the content mode. Then, get the content.

烟花易冷人易散 2024-11-01 02:49:21

我建议你在这种情况下尝试某种工厂设计模式。您可以通过注册不同的创建者来抽象内容创建过程。此外,您可以根据自己的逻辑为每个创建者添加偏好。此外,我建议您封装与内容相关的所有数据,就像其他人的帖子中的“ContentDefinition”类一样。

一般来说,您需要知道灵活性和效率之间总是需要权衡。有时你的第一个解决方案就足够好了:)

I suggest you try some kind of Factory design pattern for this case. You can abstract the content create procedure by register different creators. Moreover, you can add preference on each creator for your own logic. Besides, I suggest you encapsulate all data related to Content just like "ContentDefinition" class from other's post.

In general, you need to know that there is always a trade off between flexibility and efficiency. Sometime your first solution is good enough:)

も让我眼熟你 2024-11-01 02:49:21

好的,因为我注意到您实际上也想要 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;

} }

Ok, because I noticed a bit late that you actually wanted the ContentFrom mode as well, I've done my best to come up with a translation of your sample below my original answer


In general I use the following paradigm for cases like this. Search and replace your specific methods here and there :)

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); } }

Note how you can now make v!=null arbitrarily 'interesting' for your purposes.

Note also how lazy evaluation makes sure that the calculations are never done when _value or _alternative are set to 'interesting' values


Here is my initial attempt at putting your sample into this mold. Note how I added quite a lot of plumbing to make sure this actually compiles into standalone 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;

} }
公布 2024-11-01 02:49:21
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;
}
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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文