使用显式和隐式转换运算符改善LINQ代码的气味

It's fairly easy to learn a language - programming language or human language - and get to the point where you can be understood. Your intent may not be crystal-clear, but at least it can be figured out but the computer/listener. However, it takes time and consultations with "native speakers" to get to be really fluent and start writing poetry.

学习一种语言(编程语言或人类语言)并达到可以理解的程度是相当容易的。 您的意图可能并不十分清晰,但至少可以从计算机/听众中看出来。 但是,要真正流利并开始写诗需要花费时间和与“母语”的咨询。

ScottGu has a fine sample on how to make a Simple Web-Based RSS Reader with LINQ to XML. In it he's filling an ASP.NET ListView using RSS XML as the data source. He makes a query like this to populate a collection of posts.

关于如何使用LINQ to XML制作简单的基于Web的RSS阅读器, ScottGu有一个很好的示例。 在其中,他使用RSS XML作为数据源来填充ASP.NET ListView。 他进行这样的查询以填充帖子集合。

var posts = from item in rssFeed.Descendants("item")
    select new
    {   
        Title = item.Element("title").Value,
        Published = DateTime.Parse(item.Element("pubDate").Value),
        NumComments = Convert.ToInt32(item.Element(slashNamespace + "comments").Value,
        Url = item.Element("link").Value,
        Tags = (from category in item.Elements("category")
                orderby category.Value
                select category.Value).ToList() 
}

Seems pretty straight forward. He's grabbing a number of things from the <item> tag in the RSS and putting them into an anonymous type. See how he's using "select new" rather than "select new Foo"? My code smell doesn't like the .Values, but I'm not that worried yet.

似乎很简单。 他从RSS的<item>标记中抓取了很多东西,并将它们放入匿名类型。 看到他如何使用“选择新”而不是“选择新Foo”? 我的代码味道不喜欢.Values,但是我还不那么担心。

I added my own feed and ran his code and got a NullReferenceException on this line. Remember it's all one line, so that can be confusing. It was hard to tell which of these expressions was telling me something was null. I guessed though, that it was the line looking for <rss:comments> on the blog post. DasBlog, the engine I use, doesn't include a <comments> element when there's zero comments. Perhaps it's right, perhaps not, regardless, it doesn't include. So, for DasBlog, zero comments means no <comments> element. This code doesn't handle that because the call to item.Element(slashNamespace + "comments").Value is a NullReferenceException as item.Element(slashNamespace + "comments") is null.

我添加了自己的供稿并运行他的代码,并在此行上获得了NullReferenceException。 请记住,这全都是一行,因此可能会造成混淆。 很难说出这些表达式中的哪一个在告诉我某些东西为空。 我猜不过是在博客文章中寻找<rss:comments>的那一行。 当注释为零时,我使用的引擎DasBlog不包含<comments>元素。 也许是正确的,也许不是,无论如何,它不包括在内。 因此,对于DasBlog,评论意味着没有<comments>元素。 这段代码无法解决这个问题,因为对item.Element(slashNamespace + “ comments” ).Value的调用是NullReferenceException,因为item.Element(slashNamespace + “ comments” )为null。

So, how do I say "make NumComments = the value of <comments> unless it's null, then make it zero?" First, we tried this.

因此,我怎么说“使NumComments = <comments>的值,除非它为空,然后使其为零?” 首先,我们尝试了这一点。

NumComments = Convert.ToInt32(item.Element(slashNamespace + "comments") != null ? item.Element(slashNamespace + "comments").Value : "0")

The use of ? :, as in <expression> ? true : false is very comment in early C#. Wordy, but it works. Of course, we're indexing twice into item.Element, once to see if it's null and again to get the value it if it's not.

指某东西的用途 ? :,如<expression>中所示? true:false在早期C#中非常注释。 罗y的,但有效。 当然,我们在item.Element中建立了两次索引,一次是查看它是否为null,另一次是获取它的值。

This didn't smell right to me, but a few things smelled bad both here and before. I don't like seeing all the .Value properties. Also, the Convert.ToInt32 seemed dodgy. I kind of felt like I shouldn't have to do that, or that I should called XmlConvert, rather than Convert.

这对我来说不是很香,但是这里和以前都有几处难闻的气味。 我不喜欢看到所有的.Value属性。 另外,Convert.ToInt32似乎很狡猾。 我觉得我不必这样做,或者我应该叫XmlConvert,而不是Convert。

I also felt that I should be able to use the ?? operator, as in x = y ?? z, meaning make x = y if y's not null, else x = z. But that darned .Value property gets in the way.

我还觉得我应该可以使用?? 运算符,如x = y? z,意味着如果y不为null,则使x = y,否则使x = z。 但是那个过分的.Value属性会妨碍您。

A few newish C# things can make it cleaner (Thanks Anders).

一些新的C#东西可以使它更清洁(谢谢Anders)。

显式与隐式转换 (Explicit vs. Implicit Conversions)

Classes can use the explicit or implicit keywords to control how types are converted. You use implicit if you can guarantee that no data will be lost in the conversion and you use explicit if you can't guarantee that. Implicit conversions are like long foo = bar, where bar is an int. Ryan Olshan has some good examples like:

类可以使用显式隐式关键字来控制类型的转换。 如果可以保证在转换中不会丢失任何数据,则可以使用隐式;如果不能保证,则可以使用显式。 隐式转换就像long foo = bar,其中bar是一个整数。 瑞安·奥尔山(Ryan Olshan)有一些很好的例子,例如:

public static implicit operator int(MyClass myClass)
{
    return myClass.Value;
}

Then later, you'd do something like int x = someMyClassInstance and the conversion is implicit.

然后,您将执行类似int x = someMyClassInstance的操作,并且转换是隐式的。

Back to the LINQ to XML RSS, example there's a whole pile (24, actually) of explicit conversion operators defined for the XElement class. Here's the one for int? (Nullable int):

回到LINQ to XML RSS,示例为XElement类定义了一大堆(实际上是24个)显式转换运算符。 这是int的吗? (可为空的int):

public static explicit operator int?(XElement element)
{
    if (element == null)
    {
        return null;
    }
    return new int?(XmlConvert.ToInt32(element.Value));
}

See how it calls .Value for me? It does exactly what I want. Also, because it'll return null, now I can do this (changes in red):

看看它对我来说叫什么。价值? 它正是我想要的。 另外,因为它将返回null,所以现在我可以执行此操作(更改为red ):

var posts = from item in rssFeed.Descendants("item")
    select new 
    {   
        Title = (string)item.Element("title"),
        Published = (DateTime)item.Element("pubDate"),
        NumComments = (int?)item.Element(slashNamespace + "comments") ?? 0,
        Url = (string)item.Element("link"),
        Tags = (from category in item.Elements("category")
                orderby category.Value
                select category.Value).ToList()
    };

I changed a few calls to .Value to explicit string casts to emphasize the point since the explicit operator for string is just this. Same with DateTime where the call to Parse becomes a cast.

我将对.Value的一些调用更改为显式字符串强制转换,以强调这一点,因为字符串的显式运算符就是这样。 与DateTime相同,其中对Parse的调用变为强制转换。

public static explicit operator string(XElement element)
{
    if (element == null)
    {
        return null;
    }
    return element.Value;
}

Now before you go off on how this can be used for evil. Yes, of course it can. However, there's a very unambiguous expectation presented with explicit and implicit. You only get to use them as a class designer if you can promise they will work as expected.

现在开始讨论如何将其用于邪恶。 是的,当然可以。 但是,明确和隐含的期望值非常明确。 如果您可以保证它们将按预期工作,则只能将它们用作类设计器。

In this case, I think it makes the code much cleaner. Here's the before and after again:

在这种情况下,我认为它使代码更简洁。 这是之前和之后:

BEFORE:

之前:

var posts = from item in rssFeed.Descendants("item")
    select new
    {   
        Title = item.Element("title").Value,
        Published = DateTime.Parse(item.Element("pubDate").Value),
        NumComments = Convert.ToInt32(item.Element(slashNamespace + "comments") != null ? item.Element(slashNamespace + "comments").Value : "0"),
        Url = item.Element("link").Value,
        Tags = (from category in item.Elements("category")
                orderby category.Value
                select category.Value).ToList()     
    };

AFTER:

后:

var posts = from item in rssFeed.Descendants("item")
    select new 
    {   
        Title = (string)item.Element("title"),
        Published = (DateTime)item.Element("pubDate"),
        NumComments = (int?)item.Element(slashNamespace + "comments") ?? 0,
        Url = (string)item.Element("link"),
        Tags = (from category in item.Elements("category")
                orderby category.Value
                select category.Value).ToList()
    };

Now NumComments will handle missing <comments> elements and things look tidier. Very cool. Thanks ScottGu and Anders for their help, direct and indirect.

现在,NumComments将处理丢失的<comments>元素,并且看起来更整洁。 很酷。 感谢ScottGu和Anders的直接和间接帮助。

It's going to take a while but a new, more refined sense of smell is in my future, I think. One doesn't have to necessarily remember that ?? and conversion operators exist, but rather to have a better "sense" when reading/writing code that there ought to be a cleaner way and then go looking for it.

这将需要一段时间,但我认为,新的,更精致的气味感正在我的未来。 不必一定要记住那个?? 并且存在转换运算符,但是在读取/编写代码时应该有一个更好的“感觉”,应该有一个更简洁的方法然后再去寻找它。

翻译自: https://www.hanselman.com/blog/improving-linq-code-smell-with-explicit-and-implicit-conversion-operators

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值