面向对象的一点简易理解
简单面向对象套路
1 public class Classfather 2 { 3 public string name { get; set; } 4 public Classfather(string _name) 5 { 6 Console.WriteLine("调用了父类的构造方法"); 7 name = _name; 8 } 9 virtual public void fatherMethod() 10 { 11 Console.WriteLine("父类方法 myname is "+name); 12 } 13 } 14 15 public class Classson:Classfather 16 { 17 //我们称之为接力传递 18 //父子类之间继承 构造函数 参数通过下面这种方式接力传递到父类的 19 public Classson(): base( "father") 20 { 21 Console.WriteLine("调用了子类的构造方法"); 22 } 23 public Classson(string _name):base("father->"+_name)//_name接力传递 24 { 25 Console.WriteLine("调用了子类的构造方法"); 26 //name = _name; 27 } 28 29 30 //new 会把父类的方法废掉,父类的同名方法从此与他无关 以子类为主 你可以写 31 //跟父类名字一样参数不一样的方法,相当与重载 32 //用override关键字: 33 //方法重写,父类必须要有同名方法并且带virtual关键字 34 //并且重写的时候要遵循父类同名方法的参数 35 36 //如果在fatherMethod方法中写fatherMethod();会造成死循环 导致堆栈溢出 37 //但是如果想在Classson中访问Classfather的fatherMethod方法怎么办呢^_^ 38 //用base.fatherMethod();就行啦 39 //this关键字相当于当前实例本身 40 //base关键字相当于被继承的父类 41 //在一个类中方法同名参数不同叫重载 42 //父类与子类中方法重名叫重写(并且参数也要一样^_^) 43 44 //此方法注释后 也能调fatherMethod 只不过是父类的 ,加new关键字 则强行覆盖父类方法 45 public override void fatherMethod() 46 { 47 Console.WriteLine("子类继承的---父类的方法 myname is" + name); 48 base.fatherMethod(); 49 } 50 51 52 public void sonNewMethod() 53 { 54 Console.WriteLine("子类的新方法----"); 55 } 56 } 57 58 public void OOPTest() 59 { 60 61 //实例化时先执行父类构造方法,然后执行子类构造方法 62 Classfather c11 = new Classson();// ("zhangsan ");// ("c111"); 63 //注意了 此处执行是执行的子类方法 引用是父类引用类型 ,最基础c++里面向对象关键所在 也就这点 64 c11.fatherMethod(); 65 ((Classson)c11).sonNewMethod(); 66 //输出true 67 Console.WriteLine("c11 is Classfather: " + (c11 is Classfather)); 68 //输出true 由此可以提前确定类型 决定了你能否调用某函数 以免强制转换类型错误 69 //网上说叫什么协变 逆变 70 Console.WriteLine("c11 is Classson: " + (c11 is Classson)); 71 72 //输出false 73 Classfather c22 = new Classfather(""); 74 Console.WriteLine("c22 is Classson: " + (c22 is Classson)); 75 76 Console.WriteLine("-------------------------------"); 77 Classson c33 = new Classson(); 78 c33.fatherMethod(); 79 //输出true 80 Console.WriteLine("c33 is Classson: " + (c33 is Classson)); 81 //输出true 82 Console.WriteLine("c33 is Classfather: " + (c33 is Classfather)); 83 84 //fatherMethod() 85 //xxx c33 = new Classson(); 86 //不管前面的引用是何类型父类还是子类 只认实例化对象 new Classson(); 87 //如果子类复写了fatherMethod() c33.fatherMethod() 则调用子类,未复写则自动调用父类 88 89 //至于能不能调用 代码提示能不能出来 那是另一回事了涉及到前面说的协变逆变 强转类型,这句切记 90 //所谓设计模式也就是玩这些个东西 91 92 //以上充分体现了面向对象宽范围兼容窄范围的思想 93 }
两个互不相干的cls(哪怕在同一命名空间) 要想访问对方的成员 ,那个成员必须是public
如果是不同命名空间 或者项目 要想在其他的项目中 实例化那个cls,那个cls必须是public
要养成面向对象编程的习惯
到处都要用的变量 放到外面作为成员,
不要所有的业务逻辑全都一股脑写成一个个的方法堆起
不要访问级别 继承的什么都不管 全部写成 javascript函数形式的东西
这样的话跟oop就有点脱节了
可能开始不太习惯 但是你会发现这会给你设计程序带来莫大的好处
一个面向对象的例子
这些都是oop的基本常识 但是只要运用得当 也可以让你的程序运行得更好,别太在乎那些花哨的设计模式 并不是那些东西没用,只是别贸然模仿。时隔今日 又来这篇博文上添砖加瓦。上次张翔同学说不知道什么情况下新建一个类 什么情况下拓展,一时我也不知如何回答他。上周参考了以前的报价软件代码思想做出一个例子 正巧可以做说明:这里有个功能 要做各种报表导出。 然后就建一个 报表导出基类:
1 class ReportExport 2 { 3 string reportName; 4 string reportFullName; 5 ProjectAdapter ada; 6 //初始化模板名字 文件保存目录 和适配器 7 public ReportExport(string _reportName, string _reportFullName) 8 { 9 reportName = _reportName; 10 reportFullName = _reportFullName; 11 ada = MyApp.Instance.AdapterFlyWeight.GetProjectAdapter(); 12 } 13 15 //进行数据适配 16 public virtual void AdaptiveToData() 17 { 18 dataModels = new List<BaseModel>(); 19 templateNames = new List<string>(); 20 sheetNames = new List<string>(); 21 } 22 23 protected List<BaseModel> dataModels; 24 protected List<string> templateNames; 25 protected List<string> sheetNames; 26 28 //进行导出动作 29 public virtual bool Export() 30 { 31 ada.ExoprtToFile(dataModels, templateNames, sheetNames, reportFullName); 32 return true; 33 } 34 }
1 class ReportJiafang:ReportExport 2 { 3 public ReportJiafang(string reportJiafangFullName) 4 : base("甲方文件报表",reportJiafangFullName) 5 { } 6 7 public override void AdaptiveToData() 8 { 9 base.AdaptiveToData(); 11 dataModels.Add(App.Instance.CurrentProject); 12 dataModels.Add(App.Instance.CurrentProject); 13 14 templateNames.Add("甲方文件报表汇总表"); 15 templateNames.Add("甲方文件报表明细表"); 16 18 sheetNames.Add("甲方文件总表11"); 19 sheetNames.Add("甲方文件明细表22"); 20 } 21 }
你看我初始化的时候 自动调用基类的构造方法 名字默认传“甲方文件报表” ,因为从类的名字上已经体现了,然后只需要传保存文件路径通过base传递给基类构造函数。然后是通过 AdaptiveToData进行数据适配处理 ,不同的报表是不一样的。所以这里需要对方法进行重写。然后调用的时候:
1 ReportJiafang rpt =new ReportJiafang("d:\\aaa.xlsx"); 2 3 rpt.AdaptiveToData(); 4 rpt.Export();
你看继承和面向对象编程就是有这个威力 ,对同类型的工作进行归纳放到基类处理。而在外部调用又实现了保证接口和流程的统一性。而让子类又能够拥有各自的细节化处理。你如果用面向过程的思维方式来编程 是不是要low很多 特别是处理这种问题的时候。 是不是体现了一种辩证与统一的思想。有位大佬说过,世界是电脑运行的内存 ,类是模板 不占空间, 实例化后就是具体的对象 要在现实世界占空间,接口就是技能,他有开车的技能 那么他实现接口。感觉还是很形象化的。还有某些规律跟世界的运转规律不谋而合 ,比如普创接地系统的联动控制机制 站控如果点了手动则意思代表脱离 不再受中央控制,跟web的css样式表 ,类的virtual方法重新 都是同样的理念。还有U3d里面的3d对象树结构 然后自上而下的 属性影响 数据分配 ,WPF里的数据绑定 logicTree ,virtualTree,也都是类似理念,就像这个社会自上而下的 节点分配一样。
结构设计随想
一个例子
1 public partial class Form1 : Form 2 { 3 public static Form1 f1; 4 5 6 private void Form1_Load(object sender, EventArgs e) 7 { 8 f1 = this; 9 10 //userControl11.bindEvent(this); 11 } 12 13 public static void ShowMsg(string msg){ 14 f1._ShowMsg(msg); 15 } 16 17 private void _ShowMsg(string msg){ 18 this.Invoke(new Action(()=>{ 19 this.label2.Text = msg; 20 })); 21 } 22 23 private void btn_test_Click(object sender, EventArgs e) 24 { 25 Form1.ShowMsg("showmsg test..."); 26 } 27 }
又一个例子
窗体有很多子控件,当主窗体想触发某些订阅(定义了自己代码)了的控件动作时,那么就可以简单的直接使用c#自带的 事件和订阅 发布来处理,如果还想降低耦合 子控件在底层项目里怎么办呢,像s-manager里一样 底层项目定义Ixxx 主窗体实现接口 ,子控件初始化时把实现了接口的窗体自己传过去 ,子控件自己订阅并写事件自定义代码:
位于library项目的代码
1 public interface Iform 2 { 3 EventHandler<MyArgs> MyEvent { get; set; } 4 } 5 6 public class MyArgs:EventArgs 7 { 8 public string data { get; set; } 9 } 10 11 public partial class UserControl1 : UserControl 12 { 13 public UserControl1() 14 { 15 InitializeComponent(); 16 } 17 18 public void bindEvent(Iform f1) 19 { 20 f1.MyEvent += new EventHandler<MyArgs>((sender, args) => 21 { 22 label1.Text = args.data; 23 }); 24 } 25 private void UserControl1_Load(object sender, EventArgs e) 26 { 27 28 } 29 }
拖动用户控件到主项目,主窗体的代码:
1 public partial class Form1 : Form, Iform 2 { 3 4 5 private void Form1_Load(object sender, EventArgs e) 6 { 7 8 userControl11.bindEvent(this); 9 } 10 public EventHandler<MyArgs> MyEvent 11 { 12 get; 13 set; 14 } 15 private void btn_test_Click(object sender, EventArgs e) 16 { 17 MyEvent(this,new MyArgs(){ data= "userevent subscrip test..."}); 18 } 19 }
又一个例子
比如我们想让整个解决方案所有地方都调用一个叫ShowMsg的函数 起到中枢控制作用或直接展示信息,那么毫无疑问这个函数只能放在底层,但是上层有winform窗体我们想让ShowMsg能把信息展示到上层窗体,怎么做呢,还是只能在接口上做文章,上层继承接口把自己的实例传下去,这样调用 Logger.GetInstance().AppendMsg("loginfo test..."); 无论上下层 何处都可以进行日志记录。注意在开始的时候设置处理接口SetCurrentProc
library项目代码
1 public interface Iform 2 { 3 void AppendMsg(string msg); 4 } 5 6 public class Logger 7 { 8 private static Logger loger; 9 private Logger() 10 { 11 12 } 13 public static Logger GetInstance() 14 { 15 if (loger == null) 16 loger = new Logger(); 17 return loger; 18 } 19 20 Iform f1; 21 public void SetCurrentProc(Iform _f1) 22 { 23 f1 = _f1; 24 } 25 public void AppendMsg(string msg){ 26 if (f1 != null) 27 f1.AppendMsg(msg); 28 } 29 }
上层项目窗体代码
1 public partial class Form1 : Form, Iform 2 { 3 public void AppendMsg(string msg) 4 { 5 this.Invoke(new Action(() => { 6 this.label2.Text = msg; 7 })); 8 } 9 private void Form1_Load(object sender, EventArgs e) 10 { 11 Logger.GetInstance().SetCurrentProc(this); 12 } 13 14 private void btn_test_Click(object sender, EventArgs e) 15 { 16 Logger.GetInstance().AppendMsg("loginfo test..."); 17 } 18 }