Advertisement
Advertisement

新足迹

 找回密码
 注册
新足迹 门户 IT专业论坛 查看内容

C# 的小疑惑,让你写更美更有效的代码

2011-2-26 16:34| 发布者: dalaohu | 查看: 2936| 原文链接

We all have those little wonders in our .NET code, those small tips and tricks that make code just that much more concise, maintainable, or performant. Many of you probably already know of some of these, but many folks either overlook them or just don’t know about them, so this article is a refresher.

1. IsNullOrEmpty() VS IsNullOrWhiteSpace()
string str1 = "";
string str2 = " ";
string str3 = "\r\n";

bool res1 = String.IsNullOrEmpty(str1);
bool res2 = String.IsNullOrEmpty(str2);
bool res3 = String.IsNullOrEmpty(str3);

Response.Write(String.Format("Result 1:{0}", res1));
Response.Write(String.Format("<br />Result 2:{0}", res2));
Response.Write(String.Format("<br />Result 3:{0}", res3));

The result is: Result 1:True Result 2:False Result 3:False

2. The As Cast

How many times have you seen code like this:

if (employee is SalariedEmployee)
{
        var salEmp = (SalariedEmployee)employee;
}

This is redundant because you are checking the type twice. Once in the is check and once in the cast. Whenever you find yourself going down this path, prefer the as cast. This handy cast will cast the type if the types are compatible, or return null if not:

var salEmployee = employee as SalariedEmployee;
if (salEmployee != null)
{
    pay = salEmployee.WeeklySalary;
}


3. The Stopwatch Class

How many times have you wished to log how long a particular piece of code took to execute? Maybe you are trying to track average request time or some such metric, or maybe send a warning message when a stored procedure takes longer than 1 second to execute.

Well, you could do it with DateTime like so:
DateTime start = DateTime.Now;
SomeCodeToTime();
DateTime end = DateTime.Now;
Console.WriteLine("Method took {0} ms", (end - start).TotalMilliseconds);


The Stopwatch class makes it clean and easy to use the high resolution timer through the convenience of a C# class:

var timer = Stopwatch.StartNew();
SomeCodeToTime();
timer.Stop();
Console.WriteLine("Method took {0} ms", timer.ElapsedMilliseconds);

4. Static Class Modifier

Let’s say you’re writing an XmlUtility class, and the goal of this class is to be able to serialize an object to a string of XML without having to do the encoding and serializing each time. You may come up with something like this:

public class XmlUtility
{
        public string ToXml(object input)    {
                //XML serialization logics
                return xml;
        }
}

var xmlUtil = new XmlUtility();
string result = xmlUtil.ToXml(someObject);


This is just typical XML serialization code. The problem is, we have to create this class to use it:
That’s not very elegant usage since the class instance has no state. Of course you could avoid this by making the method static and a private constructor so that it can’t be created:
public class XmlUtility
{
        private XmlUtility() {}
        public static string ToXml(object input)
        {
                return xml;
        }
}

Well, that prevents someone from incorrectly instantiating or inheriting our class, which is good. But that empty private constructor is kind of ugly and forced, and there’s nothing that prevents a modifier from adding a non-static method by mistake:

public T FromXml<T>(string xml) {...}

Since this was not declared static, but the constructor is not visible, this method can never be used. Enter the static class modifier. If you put the word static before the class keyword, it tells the compiler that the class must only contain static methods, and cannot be instantiated or inherited.


public static class XmlUtility
{
    public static string ToXml(object input) { return xml; }
}

now it cannot be instantiated and no one can come in later and accidentally add an instance method, property, constructor by mistake!

5. Generic Delegates

The great thing about delegates is they can make classes so much more reusable than inheritance can. For example, let’s say you want to design a cache class and that class will contain a method, provided by the class’s user, that will determine if a cache item has expired and can be removed. You could provide an abstract method that the user has to provide, but this means that the user would have to extend your class and override the method, which means a lot of extra work for providing that functionality. In addition, this means you can’t mark your class as sealed to protect it from other non-related changes during inheritance.

This is where delegates become really powerful, you could provide a delegate that would specify a method type that can be called to check a cache item to see if it’s expired, and then when the user instantiates your cache, they can pass in or set the delegate to the method, anonymous delegate, or lambda they want called. This way, no subclass is necessary and in fact you can seal your class to prevent unintentional changes. This makes the class much more safe and reusable.

So what does this have to do with generic delegates? Well, there are three basic “types” of delegates that appear over and over again, and rather than having to write these delegate types repeatedly, .NET has done you a big favor by making them generic so that they can be used repeatedly. This also has the added benefit of making code that depends on delegates more readable, as these generic delegates are well understood in usage and intention:

•Action – A method that takes an argument of type T and returns void – generally used to perform an action on an item.
•Predicate – A method that takes an argument of type T and returns a bool – generally used to determine if an item meets a condition.
•Func – A method that returns a result of type TResult – generally used to generate values.

Note that these are the base forms of these generic delegates, and there are versions that take multiple parameters for each, the main thing that is constant is that Action returns void, Predicate returns bool, and Func returns a specified result.

So, to our cache example, say you wanted to write that cache that accepts a caching strategy, you would want a delegate that operates on an argument and returns a bool as to whether it has expired, that sounds like a Predicate:

public sealed class Cache<T>
{
        private ConcurrentDictionary<string, CacheItem<T>> _cache;
        private Predicate<CacheItem<T>> _expirationStrategy;
        public Cache(Predicate<CacheItem<T>> expirationStrategy)
        {
                 // set the delegate
                 _expirationStrategy = expirationStrategy;
        }
}

var cache = new Cache<int>(item => DateTime.Now - item.LastAccess > TimeSpan.FromSeconds(30));


In fact, we can create as many caches we want with as many expiration strategies as we can think of and never need to subclass! Know and use your generic delegates and it will really increase the re-usability of your classes. In fact, any time when you are thinking of adding an abstract method and needing to provide functionality in a sub-class, if all you are doing is providing “hooks”, consider generic delegates instead.

[ 本帖最后由 dalaohu 于 2011-2-26 18:51 编辑 ]
Advertisement
Advertisement


Advertisement
Advertisement
返回顶部