【设计原则和建议】 方法返回值
基本规则
1.不要忽略返回值;如果不想处理返回值,就选择没有返回值的方法(如果有的话)
2.公开给第三方的方法返回值类型在满足功能的情况下,尽量选择父类和接口,而不是具体类型 (面向对象的封装性)
- 这点可能有很大的争议,我区分为对外的和对内的方法 (对内请看No.3)
- 返回子类,意味要把子类的细节也暴露出去
- 使用接口返回具体类型,就不用暴露细节了
public class ClassP //请忽略嵌套类的设计问题
{
protected internal class User : IUser//我不想暴露User类的细节给外部 注:也可能是private等的访问性
{
public void ResetMoney() { }
}
public IUser GetUser()
{
return new User();
}
}
public interface IUser
{
}
class Program
{
static void Main(string[] args)
{
ClassP p = new ClassP();
var user = p.GetUser();
}
}
- 下面又是一个返回具体类型的悲剧(一般不建议直接暴露内部集合成员,而是使用Clone等方法返回副本,或者使用 IEnumerable<T>)
class Program
{
static void Main(string[] args)
{
ClassS s = new ClassS();
var list = s.GetList();
s.PrintListCount();
list.Add("string");
s.PrintListCount();//这里输出1个, ClassS的内部变量被改变了,破坏了封装性
}
}
public class ClassS
{
private List<string> list = new List<string>();
public List<string> GetList()//bad
{
return list;
}
public IEnumerable<string> GetList2()//good
{
return list;
}
public void PrintListCount()
{
Console.WriteLine(list.Count());
}
}
- 有时候为了保持行为的一致性而选择返回父类 ( WebRequest HttpWebRequest.Create(string requestUriString);)
3.内部方法(如private)尽可能选择详细类型
- 调用方得到更多细节信息,可以做更多的操作
- 除非是设计上使用了工厂模式等设计方式,那只能返回接口或者父类了 (返回类型是父类,实际类型还是子类)
4.建议将返回值存在本地变量以后再使用
- 链式表达式是例外情况
5.将接口作为返回值,往往意味着该设计希望解耦合
6.别为了方便或者是懒惰把方法返回值都弄成Object
7.优先使用返回值,而不是ref和out参数
8.参数类型和方法名保持一致
- 如果是链式表达式,自然返回值类型和类本身保持一致
IQueryable<ClassX> list = null;//只是为了演示 所以没有值
var data = list.OrderBy(p => p.Age).Where(p => p.Type == 1).ToList();
返回值和异常
1.为什么选择异常
- 一般情况下,在内部使用的方法中,使用异常来提示执行错误,而不是用返回值
- 所谓内部使用的意思是:该方法不是公开给第三方使用的(例如 组件开发中的public方法, 例如WebService 等)
- 异常不影响正常逻辑代码的阅读,也不影响返回值本身的意义(例如 你不需要声明一个返回值的类 同时返回 状态码 异常信息 和真正的数据)
- 相比于返回值,异常的功能信息更为丰富,例如携带堆栈跟踪等
- 相比于返回值,异常的功能更为强大, 例如通过 AppDomain.CurrentDomain.UnhandledException 处理所有未处理的异常
- 使用异常来告知执行失败和.net类库本身保持行为一致 例如: System.IO.File.WriteAllText() ;
- 如果使用返回值类通知执行失败,用户容易忽略返回值,如下所示
static void Main()
{
GoGoGo();//没注意到返回值是false
//继续....
}
static bool GoGoGo()
{
return false;
}
2.为什么选择返回值
- 外部使用方法中应该使用返回值而不是异常,包括但不仅限于Socket,HTTP,跨语言,跨进程的通信
- asp.net本身 还有WCF 都提供了全局的handler将错误转化为返回的html或者xml (就是我们经常见到的黄色错误页面) 在程序内部使用异常,准备返回的时候使用统一的handler处理为返回值是一个较好的实践
- 性能问题; 就是为了性能问题.net类库提供了 Int.TryPrase 方法
返回值类型和值
1. void无返回值
- 不要为了增加返回值而增加返回值,一个东西它如果逻辑上不需要返回值那么就应该设计为void
2.Int 和其他所有的数值类型
- 在数据逻辑上只能是正整数0的时候,使用-1 作为条件不成立的值. 例如String.IndexOf 返回-1代表字符串不存在
- 在数据逻辑上是整数的时候,使用 Int? (Nullable<int>) 并且值为null 作为条件不成立. 例如 int? GetUserId() 用户不存在的时候返回Null (更推荐的方法是先调用方法判断用户是否存在, 然后调用 int GetUserId())
3.String
- 将null作为条件不成立或者无数据的表示
- 传递给表现层的返回值考虑返回空字符串 "" (String.Empty) 优先于null (例如在asp.net页面上直接调用 s.Trim() 等方法会比较方便,不需要判断null值) 我个人觉得这不算一个严谨的设计 但是会方便编码
4.所有普通的Class (非集合类)
- 将null作为条件不成立或者无数据的表示
5.集合类
- 将集合类型不为null但是数量为0的集合,作为条件不成立或者无数据的表示
6.泛型
- 将Default(T) 作为条件不成立或者无数据的表示
部分内容引用自MSDN,FxCop 和其他第三方文章..
因为本人水平有限,如有遗漏或谬误,还请各位高手指正