改善C#编程的50个建议(21-25)

-------------------------翻译 By Cryking-----------------------------
-----------------------转载请注明出处,谢谢!------------------------ 

21 限制类型的可见性

不是每个类型都需要Public。你应给你的类型最少的可见度来达到你的目的,内部或私有类能实现公共的接口。很多独立的类应当创建为内部的。你可以使用protected或私有嵌套类进一步来限制其可见性,可见性越小,当你之后更新时,整个系统的更改也就越少,越少的地方访问你的代码,你修改的地方也就越少。
  只暴露那些需要暴露的。对于可见性小的类,尝试使用公共接口来暴露。
// For illustration, not complete source
public class List<T> : IEnumerable<T>
{
    private class Enumerator<T> : IEnumerator<T>
    {
        // Contains specific implementation of
        // MoveNext(), Reset(), and Current.
        public Enumerator(List<T> storage)
        {
            // elided
        }
    }
    public IEnumerator<T> GetEnumerator()
    {
        return new Enumerator<T>(this);
    }
    // other List members.
}

客户端代码不需要知道类的Enumerator<T>。
.NET很多集合类也是这样设计的,如Dictionary<T>包含了私有的DictionaryEnumerator<T>,Queue<T>包含了QueueEnumerator<T>.
  创建internal类是一种被忽视的限制类型范围的方法。缺省情况下,大部分程序员总是创建Public类,没有去考虑其他,因为这些都是VS .NET向导做的事。在接收默认形式的时候,你应留心你的新创建的类会用在哪里?是所有客户端还是主要在程序集内部使用?
  使用接口暴露你的函数使你更容易创建internal类,如:
public interface IPhoneValidator
{
  bool ValidateNumber(PhoneNumber ph);
}
internal class USPhoneValidator : IPhoneValidator
{
	public bool ValidateNumber(PhoneNumber ph)
	{
	  // perform validation.
	  // Check for valid area code, exchange.
	  return true;
	}
}


22 偏爱定义和实现接口而不是继承
抽象基类提供了类层次的祖先。一个接口描述了一个能被类实现的原子功能。
接口以一种契约的方式来设计:一个实现了接口的类必须提供一个预期的方法实现。抽象基类为一系列相关的类型提供了一个公共抽象方法。继承意思是是什么,接口意思是行为像什么.这都是老生常谈的问题,但也是它们的工作。它们描述两个不同的概念:基类描述的是对象是什么,接口描述的是对象的一种行为方式。
  接口描述了一组功能或者说是一个契约。你可以在接口中创建方法、属性、索引器、事件等。你也可以创建接口所没有的方法,因为方法没有具体的数据成员,你可以使用扩展方法来扩充接口功能。如扩展泛型接口IEnumerable<T>:
    public static class Extensions
   {
	public static void ForAll<T>(
		this IEnumerable<T> sequence,
		Action<T> action)
	{
		foreach (T item in sequence)
		action(item);
	}
   }
        // usage
foo.ForAll((n) => Console.WriteLine(n.ToString()));

抽象基类可以为它的派生类提供一些公共的实现方法,它通过virtual方法来为派生类指定数据成员、具体方法等。这个基类实现带了一个好处:如果你给基类增加一个方法,那么它的所有派生类都自动隐式地增加.但是给接口增加一个方法将会导致实现它的所有类都产生错误.选择一个抽象基类还是一个接口,这涉及到如何能最好地总是支持你的抽象功能.接口总是固定的:你发行一个接口作为一系列任何类都能实现的功能的契约.基类总是能被扩展的.这些扩展也会成为其派生类的一部分.
  这两种模型是可以混合使用的.一个例子是.NET框架中的IEnumerable<T>接口和System.Linq.Enumerable类.System.Linq.Enumerable类包含了基于System.Collections.Generic.IEnumerable<T>接口的大量扩展方法。
  接口可以被任意数量的不相关类型来实现。编写接口给编写基类的开发者提供了非常大的灵活性。因为.NET环境中只支持单继承,而接口解决了多继承问题。使用接口还可以使你偶尔能减少structs类型拆箱的代价。如:
public struct URLInfo : IComparable<URLInfo>, IComparable
{
    private string URL;
    private string description;
    #region IComparable<URLInfo> Members
    public int CompareTo(URLInfo other)
    {
        return URL.CompareTo(other.URL);
    }
    #endregion
    #region IComparable Members
    int IComparable.CompareTo(object obj)
    {
        if (obj is URLInfo)
        {
            URLInfo other = (URLInfo)obj;
            return CompareTo(other);
        }
        else
            throw new ArgumentException(
            "Compared object is not URLInfo");
    }
    #endregion
}

任何基于IComparable的代码将会减少装箱和拆箱,因为可以调用IComparable.CompareTo方法而不用拆箱对象.


23 理解接口方法和虚拟方法的不同
刚开始,可能以为实现一个接口和重写一个虚拟方法一样,但实际上实现一个接口和重写虚拟方法是大不相同的.首先在接口中的成员方法并不是默认以virtual声明的.派生类不能重写基类中已实现的接口成员,接口可以显示实现,这样可以隐藏从类继承而来的公共接口。它们是在不同的用途是不同的概念。
  你可以在派生类中修改接口的实现。如:
interface IMsg
{
    void Message();
}
public class MyClass : IMsg
{
    public void Message()
    {
        Console.WriteLine("MyClass");
    }
}
Message()方法现在是MyClass类的公共接口。Message也可以通过IMsg指针来访问。现在我们来增加一个派生类:
public class MyDerivedClass : MyClass
{
    public void Message()
    {
        Console.WriteLine("MyDerivedClass");
    }
}

因为MyClass.Message()不是虚拟的,派生类是不能提供一个重写的Message()版本的。此时派生类创建了一个新的Message()方法,它把基类的Message()方法给隐藏了,相当于new void Message()。通过IMsg引用MyClass.Message仍然是可以用的:
MyDerivedClass d = new MyDerivedClass();
d.Message(); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message(); // prints "MyClass"
当你实现一个接口,你实际在类中声明了一个特殊契约的具体实现。但是通常我们想在基类创建、实现接口,然后在派生类中修改它:
    public class MyClass : IMsg
    {
        public virtual void Message()
        {
            Console.WriteLine("MyClass");
        }
    }
    public class MyDerivedClass : MyClass
    {
        public override void Message()
        {
            Console.WriteLine("MyDerivedClass");
        }
    }

MyDerivedClass以及所有从MyClass派生的类都能声明它们自己的Message方法。这个重写版本能被派生类引用访问、能被接口引用访问、能被基类引用访问,如果你不喜欢虚函数的概念,你可以这样来重新定义基类:
public abstract class MyClass : IMsg
{
public abstract void Message();
}
对,你可以在基类中不实际实现一个接口,声明它为抽象方法。这样所有派生类必须实现这个接口方法。派生类可以通过密封该方法来阻止它自己的派生重写它:
    public class MyDerivedClass2 : MyClass
    {
        public sealed override void Message()
        {
            Console.WriteLine("MyDerivedClass");
        }
    }

另一种解决方法是实现接口,然后包含一个调用该虚方法的方法,如:
    public class MyClass2 : IMsg
    {
        protected virtual void OnMessage()
        {
        }
        public void Message()
        {
            OnMessage();
            Console.WriteLine("MyClass");
        }
    }

任何派生类都能重写OnMessage方法,还可以在Message方法中增加它们自己的工作。.NET事件方法几乎都用这种模式。
  基类能提供一个缺省的接口方法实现,然后派生类可以通过继承基类声明并实现接口方法,如:
    public class DefaultMessageGenerator
    {
        public void Message()
        {
            Console.WriteLine("This is a default message");
        }
    }
    public class AnotherMessageGenerator :
    DefaultMessageGenerator, IMsg
    {
        // No explicit Message() method needed.
    }

注意派生类能声明这个接口作为它自己契约的一部分,即使它没有提供IMsg方法的实现,它通过继承基类的方法,隐式实现了该接口方法。


24 使用委托来表示回调
  回调函数用来从服务器向客户端异步地提供一个反馈。它们可能涉及多线程,或者简单提供一个同步更新的入口点。回调函数在C#中通过委托来表现。
  委托提供一个类型安全的回调函数定义。虽然委托用的最多的是事件,但这不是委托唯一能用的地方。委托让你在运行时配置目标,并且通知多个客户端。一个委托就是一个包含方法引用的对象。包含的方法可以是静态方法或者实例方法。使用委托,你可以在运行时与一个或多个客户端对象交流、配置。
  C#提供了一个紧凑的语法(lambda表达式)来表达委托。.NET框架库定义了很多常见的委托形式,如:Predicate<T>, Action<>, and Func<>.predicate是一个用来测试条件的布尔函数。Func<>需要一些数值参数,然后产生一个结果。也就是Func<T, bool>和Predicate<T>有相同的形式。Action<>输入任何数量的参数,然后返回一个void类型。LINQ就是建立在这些概念上的。
  List<T>类也包含了很多利用回调函数的方法,如:
List<int> numbers = Enumerable.Range(1, 200).ToList();
var oddNumbers = numbers.Find(n => n % 2 == 1);
var test = numbers.TrueForAll(n => n < 50);
numbers.RemoveAll(n => n % 2 == 0);
numbers.ForEach(item => Console.WriteLine(item));
Find()方法接收一个委托,以Predicate<int>的形式来测试列表中的每一个元素。这是一种简单的回调。List.ForEach()方法在每个列表元素中执行指定的动作。实际上编译器将lambda表达式转换为一个方法,然后创建一个委托来指向该方法.所有的LINQ都是建立在委托上的。在WPF和Windows Form中,回调是用来处理跨线程的封送处理的。
  由于历史原因,所有的委托都是多播委托。多播委托封装所有的目标函数然后添加在一个调用里。这种构造存在两个问题:一个是在面对异常时不安全,另一个是返回值将是最后一个目标函数调用的返回值。


25 实现事件模式的通知
  .NET的事件模式,无非是观察者模式的一个语法转换而已。事件定义了你的类的通知。事件基于委托提供了一个类型安全的函数签名。
  考虑一个简单的例子。你已经建立了一个log类,它是一个应用的所有消息的调度器。它将接收应用的所有消息,并且调度这些消息到对其感兴趣的监听者上。这些监听者可能是依附在控制台、数据库、系统日志等等,定义如下:
public class LoggerEventArgs : EventArgs
    {
        public string Message { get; private set; }
        public int Priority { get; private set; }
        public LoggerEventArgs(int p, string m)
        {
            Priority = p;
            Message = m;
        }
    }
    public class Logger
    {
        static Logger()
        {
            theOnly = new Logger();
        }
        private Logger()
        {
        }
        private static Logger theOnly = null;
        public static Logger Singleton
        {
            get { return theOnly; }
        }
        // Define the event:
        public event EventHandler<LoggerEventArgs> Log;
        // add a message, and log it.
        public void AddMsg(int priority, string msg)
        {
            // This idiom discussed below.
            EventHandler<LoggerEventArgs> l = Log;
            if (l != null)
                l(this, new LoggerEventArgs(priority, msg));
        }
    }

AddMsg方法展示了合适的抛出异常的方式。在多线程程序的静态条件上引用log事件句柄的临时变量是很重要的保护措施。C#编译器为事件创建了add和remove访问器:
public event EventHandler<LoggerEventArgs> Log
{
  add { log = log + value; }
  remove { log = log - value; }
}
log的使用:
    class ConsoleLogger
    {
        static ConsoleLogger()
        {
            Logger.Singleton.Log += (sender, msg) =>
            {
                Console.Error.WriteLine("{0}:\t{1}",
                msg.Priority.ToString(),
                msg.Message);
            };
        }
    }

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的影城管理系统,源码+数据库+论文答辩+毕业论文+视频演示 随着现在网络的快速发展,网上管理系统也逐渐快速发展起来,网上管理模式很快融入到了许多生活之中,随之就产生了“小徐影城管理系统”,这样就让小徐影城管理系统更加方便简单。 对于本小徐影城管理系统的设计来说,系统开发主要是采用java语言技术,在整个系统的设计中应用MySQL数据库来完成数据存储,具体根据小徐影城管理系统的现状来进行开发的,具体根据现实的需求来实现小徐影城管理系统网络化的管理,各类信息有序地进行存储,进入小徐影城管理系统页面之后,方可开始操作主控界面,主要功能包括管理员:首页、个人中心、用户管理、电影类型管理、放映厅管理、电影信息管理、购票统计管理、系统管理、订单管理,用户前台;首页、电影信息、电影资讯、个人中心、后台管理、在线客服等功能。 本论文主要讲述了小徐影城管理系统开发背景,该系统它主要是对需求分析和功能需求做了介绍,并且对系统做了详细的测试和总结。具体从业务流程、数据库设计和系统结构等多方面的问题。望能利用先进的计算机技术和网络技术来改变目前的小徐影城管理系统状况,提高管理效率。 关键词:小徐影城管理系统;Spring Boot框架,MySQL数据库
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值