面向OOP之路:工程师的观点

免责声明



除了从“绝对零”研究这种材料的角度出发,本文并不暗示对事物有任何根本的新看法。





该材料基于大约7年前的笔记,当时我在没有IT教育的情况下学习OOP的道路才刚刚开始。那时,MATLAB是主要语言,后来我改用C#。



我发现有关OOP原理的陈述,例如一些苹果,梨是从“水果”类继承而来的,并有许多术语(继承,多态性,封装等),被认为是中文字母。



相反,由于某种原因,我现在通常可以理解这些材料,有时我自己的文章中的演讲似乎令人困惑且冗长。



但是我的旧笔记和pipboy中磁盘上残存的可怕代码表明,“经典”演示文稿当时没有实现其功能,并且完全没有成功。也许其中有些东西。



这多少与现实和您的喜好相对应-自己决定...



OOP的前提条件



墙码



当我刚开始用MATLAB'e编写代码时,这是唯一的编写和知道如何编写代码的方法。我知道函数,并且程序可以分为几部分。



问题是所有例子都糟透了。我打开了某人的课程书,看到那里只有2-3行的小身体功能,所有这些都不起作用(有些东西不见了),只有当我将这种垃圾重新组装成“墙”时,它才起作用。



然后我几次写了一些小程序,每次我都想知道为什么要分享一些东西。直到后来才有了理解:代码“ wall”-这是大约1.5个A4页面的程序的正常状态。没有功能,上帝禁止,那里没有OOP。



这就是Matlab脚本的外观(从Internet上获取)。



Fs = 1000;                   % Sampling frequency
T = 1/Fs;                      % Sample time
L = 1000;                      % Length of signal
t = (0:L-1)*T;                % Time vector
% Sum of a 50 Hz sinusoid and a 120 Hz sinusoid
%x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t); 
%y = x + 2*randn(size(t));     % Sinusoids plus noise
y=1+sin(100*pi*t);
plot(Fs*t(1:50),y(1:50))
title('Signal Corrupted with Zero-Mean Random Noise')
xlabel('time (milliseconds)')
figure
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
plot(f,2*abs(Y(1:NFFT/2+1))) 
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')


将代码划分为功能



我猜为什么代码仍然被分成几部分,我猜想它的体积何时开始变得完全难以想象(现在我在存档中发现了垃圾代码-墙上有650行)。然后我想起了这些功能。我知道他们使您可以将代码分成小块,以便于调试和重用。



但是窍门却不一样-出于某种原因,所有教材都对变量的功能没有任何了解



。数学过程中,函数y = f(x)



称为“一个变量的函数”。例如,y = x 2是一个整数!

数学问题:按分数构造一个PARABOL。在笔记本纸上或盒子里。

. z=f(x,y). — — . , .. . .









, « », . , , . – . .



-…



图片


并且如果函数具有四个或更多变量...。超弦理论。卡拉比丘品种。凡人。没有给。理解...



总之,这都是错误的。在编程中,功能的正常状态是双阴道双肛门它需要100个变量并返回相同的值,这很好。另一件事是异常的-用COMMA列出它们。



图片


关于您可以用不同的方式写的事实,我意识到当我在这里海军




function work = SelectFun(ProtName,length_line,num_length,angleN_1,angleN_2,num_angleN,angleF_1,angleF_2,num_angleF, res_max, num_res,varargin)
global angleF angleN model_initialized


一堆由逗号分隔的变量。而且调用代码的这些参数名称完全不同,例如SelectFun(a,b,c,d...。)因此,您需要记住哪个变量在哪里。并通过COMMA进行安排。而且,如果代码正在现代化,并且变量数量发生了变化,则必须使用COMMA重新排列它们。



为何在此squalor中使用全局变量(拍摄!)?



答对了!为了不与每个代码一起排列变量,请通过COMMA进行升级。



但是COMMA仍然像噩梦一样跟着我。



图片


和varargin出现了。这意味着我可以在调用代码中添加更多的参数,并以逗号分隔...



然后,我想到了数组。教程示例兴奋地讨论了数组可以像这样的事实:




=
[1 2 3
 4 5 6
 7 8 9]


您会看到X(2,3)= 6,X(3,3)= 9,我们...我们可以在这样的数组上组织矩阵乘法!在上一课中,我们学习了PARABOLS,现在是MATRIXES…。



这些他妈的教科书中没有哪一行是简短明了的:您需要数组才能构成100个变量的函数,并且不能通过COMMA从它们的列表中脱颖而出。



图片


通常,我想到了将所有内容填充到一个大的二维表中的想法。起初一切顺利:




angles =
[angleN, angleN_1, angleN_2, num_angleN
 angleF, angleF_1, angleF_2, num_angleF]

function work= SelectFun(ProtName, length_line, num_length, angles , res_max, num_res, varargin)


但是我想要更多。它开始看起来像这样:




data=
[angleN, angleN_1, angleN_2, num_angleN
 angleF, angleF_1, angleF_2, num_angleF
length_line, num_length,  0, 0 
res_max,num_res, 0,0]
function work= SelectFun(ProtName,data,varargin)


一切似乎都很好,但是...零!它们出现是因为我想将异构数据散布在不同的行上,并且不同类型的数据量也有所不同……并且该函数应如何处理这些零?如果我想更新代码会怎样?我必须重写函数中这些讨厌的零的处理程序!毕竟,某些变量实际上可以等于零...



我从来没有要求过...



通常,这是我学习结构的方式。



结构体



在这里有必要开始介绍数据打包方法。显然,历史上首先出现了带有“表”的数组,并且它们在开始时也对此进行了介绍。在实践中,您可以找到许多程序,其中数组作为“表”是一维的,或者根本不是一维的。

结构是大约在计算机硬盘上的数据的“文件文件夹”包装。

驱动器D:\

X(变量文件夹-“对象”或“结构”)

-a.txt(带有数据的变量文件-“对象字段”,英文字段。存储5号)

-b.txt(存储10号) )

-.txtY

(变量子文件夹-“对象”)

-d.txt(存储2号)

-e.txt



为了更加清楚,让我们写下如何在Windows资源管理器中看到d.txt文件的路径

D:\ X \ Y \ d.txt


之后,我们打开文件并在其中写入数字“ 2”。

现在-它在程序代码中的外观。无需引用“根本地驱动器”,因此D:\根本就不存在,我们也没有文件扩展名。至于其余部分,在编程中通常使用句点而不是斜杠\。

原来是这样的:




X.Y.d=2
%   
X.a=5
X.b=10 
 - 
X.c=X.a+X.b    %..  .=5+10=15
X.Y.e=X.c*X.Y.d    %.. X.Y.e=15*2=30


在matlab中,可以在现场创建结构(struct),而无需离开结帐处,即上面的代码是可执行的,您可以将其驱动到控制台中,然后一切都将立即运行。该结构将立即显示,并且所有“变量文件”和“变量子文件夹”将立即添加到此处。不幸的是,不可能说关于C#的结构(struct)是由痔疮设置的。



该结构比TABLE ARRAY凉爽,这里是索引而不是索引-文件文件夹系统。结构=“变量文件夹”,其中包含“变量文件”和其他“变量文件夹”(即子文件夹的种类)。



一切都很熟悉,一切都与计算机,文件夹,其中的文件完全相同,只是文件中没有图片,但有编号(尽管也可以有图片)。



与制作阵列表的想法相比,这是用于传递给FUNCTION的数据存储的更高级版本,尤其是二维表,而令我烦恼的是tesseract,三维和多维。

ARRAY TABLE在两种情况下可用:

-它很小(为什么呢?什么,不能将用逗号分隔的参数传递给函数?)。

-您可以在其上循环并自动执行搜索/填充(并非总是可能的)

实际上,ARRAY TABLE通常仅用作同类数据的一维行。普通程序中的所有其他操作都是根据“文件文件夹”方案完成的。



那么为什么编程教科书从数组和表开始呢?



简而言之,“为我自己发现了”这些结构,我决定找到一个金矿并紧急改写了所有东西。狗屎的代码开始看起来像这样:




Data.anglesN=[angleN, angleN_1,angleN_2, num_angleN]; %  
Data.anglesF=[angleF, angleF_1, angleF_2, num_angleF]; %  
Data.length_line= length_line;
Data.num_length= num_length;
Data.res_max= res_max;
Data.num_res= num_res;
function work= SelectFun(ProtName,Data,varargin)


是的,您可以在这里做完美主义并制作一堆嵌套的对象,但这不是重点。最主要的是,现在在函数内部,该变量的索引不是按其序号(在参数列表中的位置,用逗号分隔),而是按名称进行索引。而且没有零哑。现在函数调用已经可以接受了,只有2个命令,您可以平静地呼吸。



班级



对我“下课”放倒一吨术语的概念:封装,继承,多态,静态的方法,字段,属性,普通方法,构造......#@%!!!

出经验不足,已经想通了与结构,我决定,有没有需要复杂的实体不必要地思考:“类就像相同的结构,只是更复杂”。



在某种程度上是这样。更确切地说,这就是事实。如果您看得很深,则一类是STRUCTURE(表的数组的意识形态后代),它是在程序启动时创建的(通常,似乎并且不仅在启动时)。如同在ARRAY TABLE的任何后代中一样,数据存储在此处。程序运行时可以访问它们。



因此,我的第一堂课是这样的(我正在用C#编写示例,静态字段通常不在matlab中实现,只能通过静态函数中具有持久变量的hack曲线来实现)。



public class Math{
	public static double pi;
	public static double e;

	public static double CircleLength(double R){   //.. « »
	return 2*Math.pi*R; //  
    }
}


上面的情况实际上是类的“基本”技能-愚蠢地是一个带有数据的数组(结构)。在程序开始时,将这些数据扔给它,然后可以从那里将它们提取出来,就像我们将它们从上面的结构中拉出一样。static关键字用于此->



结构可以在任何地方创建,并可以随时存储输入的数据,



->是程序启动时创建的结构。像正常结构一样,所有标有“静态”一词的字段仅存储数据。静态方法只是从类调用的函数,就像在文件夹中一样。




double L=Math.CircleLength(10); //L=62,8
Math.pi=4; //


我有一个困惑-如果字段是变量,方法是函数,那么它们如何存储在一个地方?据我了解,类中的函数(方法)实际上不是函数,而是指向函数的指针。那些。就其使用而言,它与pi几乎相同。

简而言之,一开始我就完全理解了该卷中的类,并编写了shit代码的另一部分,其中仅使用了静态函数。否则,作为带有函数的文件夹,我根本不使用类。



这一点也因以下事实而得到促进:这正是在MATLAB中完成类的方式-像这样一个愚蠢的文件夹,其名称以@开头(例如@ Math,不带空格),在其中,扩展名为.m的实际文件是函数(方法),并且存在扩展名为.m的头文件,该文件说明CircleLength函数确实属于该类,而不仅仅是带有非OOP函数的.m文件。

@ Math%文件夹

-Math.m%头文件

-CircleLength.m%函数文件

是的,对于普通人来说,有一种更熟悉的方法可以在一个.m文件中编写类,但是起初我并不知道。matlab中的静态字段仅是常量,并且在程序启动时被写入一次。可能是为了防止“拖网”,该拖网决定分配Math.pi = 4(恕我直言,绝对无用且愚蠢的主题,没有正常的人会在matlab中编写大型项目,而程序员将调试小型项目,因此,这不太可能他是个白痴)。



但是回到主题。除了静态方法外,该类还具有构造函数。构造函数基本上只是一个函数,例如y = f(x)甚至y = f()。它可能没有输入参数,必须有输出参数,并且这始终是一个新结构(数组)。



构造函数的作用。他只是制造结构。逻辑上看起来像这样:



C#代码 近似布尔等效项(伪代码)


class MyClass {
    int a;
    int b;
    public  MyClass() {
	this.a=5;
	this.b=10;
    }
}



class MyClass {
    public  static MyClass MyClass() {
        int this.a=5;
        int this.b=10;
        return this;
    }
}



//… -   
var Y=new MyClass();	



//… -   
var Y= MyClass.MyClass();	






在matlab上执行代码,使类似的结构没有任何类(存在该类的地方-见下文):




function Y=MyClass() %  MyClass,   Y=F()
    Y.a=5
    Y.b=10
end
… -   
Y=MyClass()


在输出处,我们具有以下结构

Y(文件夹变量)

-a(文件变量等于5)

-b(文件变量等于10)

由此可见,实际上很明显,所谓的类字段(不是静态的,没有静态键码)是在构造函数内部声明的局部变量。它们是为某种恶魔而不是在构造函数中而是在外部编写的,这是SYNTAXIC SUGAR。



SYNTAX SUGAR-编程语言的此类胡扯功能,当代码开始看起来像是在编写代码时希望对其进行混淆时。但是,另一方面,它变得越来越短(据称)。



做完这个“发现”之后,当时只在Matlab写作的我感到非常惊讶。



如上所述,在matlab中,只需编写Ya = 5Yb = 10即可就地创建这些结构,而无需任何构造函数,就像您在操作系统中可以在不离开收银机的情况下制作文件和文件夹一样。



在这里-某种“构造函数”,以及结构的所有字段(在Matlab中,它们都称为属性-属性,尽管严格地说,属性比字段更脏)必须在头文件中官僚地编写。做什么的?我在该系统中看到的唯一好处是,结构字段是预定义的,就像“自我文档”一样-您始终可以看到应该存在的内容和不应该存在的内容。我当时写的是这样的:




classdef MyClass
    properties %   
        a
        b
    end
    methods % 
        function Y=MyClass() %  . 
        %    () Y   a, b
            Y.a=5;
            Y.b=10;
        end
    end
    methods (Static) %  
        function y=f(x) %  
            y=x^2; %    ,    !11
        end
    end
end


那些。您正确理解了所有方法:这些方法只是静态的,xs构造函数是针对什么的(它写在文档中-哦,类必须有一个构造函数-嗯,这是您的构造函数),我愚蠢地一无所知,因此决定学习了Zen和OOP。



但是,在我看来,通过类文件夹收集函数(静态方法)似乎是一个很不错的主意。他们很多,我坐下来写狗屎代码。



官僚



并遇到了这样的事情。有一些逻辑较低级别的函数集(它们是静态的,并且包装在类文件夹中,现在我们将省略类的名称):




Y1=f1(X1);
Y2=f2(X2);
Y3=f2(X3);
Y20=f20(X20);


在小型项目中,不可能实现这样的功能优势,教育示例通常包含2-3个功能-例如“看看如何构建PARABOL”。



在这里-一团糟的功能,每个母亲,每个人都有一个输出参数,如何处理它们呢?引入更高(“领先”)逻辑水平的功能!通常,它们的数量要少得多(通常是20个,而不是5个)。那些。通常,您需要以某种方式将这些Y1,Y2,Y3….Y20删除,然后将其删除到Z1,Z2…Z5中。这样以后您就可以召开聚会会议了:




A1=g1(Z1);
A2=g2(Z2);
A5=g5(Z5);
% ,  .  , !


但是Z1…Z5不仅是自己带来的。要创建它们,您需要FUNCTIONS-PACKERS。按照惯例,他们的工作方式是这样的...




function Z1=Repack1(Y1,Y7, Y19)
    Z1.a=Y1.a+Y7.b*Y.19.e^2;
    Z1.b=Y7.c-Y19.e;
    %....  -      Y1, Y7, Y19 
    %    Z1. 
    %        Z2…Z5, 
    % 4 .  !
end


然后可能还有另一个“管理”级别……



简而言之,我意识到自己陷入了一个物流地狱。我通常无法在不编写重新包装官僚函数的图云的情况下从小函数y = f(x)的图云中提取数据,并且当数据传输到更高级别时,我们需要更多的替换者。最终程序充斥着官僚机构-重新包装器比“业务代码”多。功能文件夹类不能解决此问题,它们只是将官僚白痴重新打包器收集在堆中。



然后,我决定对这个糟糕的代码进行现代化处理,结果证明,如果不看整个官僚机构的一部分,这是不可能的!



就像在俄罗斯生活一样...



我意识到自己做错了事,并且对OOP有了更好的了解。解决方案-如果您以这种方式看待它,那么它就在意识形态上。



面向对象的想法



当可以创建一个参数时, 为什么要使用一堆函数y = f(x)产生不同的输出参数Y1….Y20有点:




Y_all=f1(Y_all, X1); 
Y_all=f2(Y_all, X2);
….
Y_all=f20(Y_all, X20);


然后,所有功能的绝对结果将被推入一个结构,一个数组中,恰好进入其不同的部分。所有。然后,Y_all可以直接传送到“管理”的上层。




Y_all=DO_MOST_IMPORTANT_SHIT(Y_all, options_how_to_do_this_shit)


所有所有功能-SEALERS-BUREAUERS齐聚一堂!所有数据都在一个Y_all库中收集,所有低级功能将其工作成果放在不同的Y_all隔室中,“管理”遍及所有Y_all隔室,并应做的事情。没什么多余的,代码可以快速编写并且可以很好地工作...



这恰恰是OOP的构想,由它组成。在教科书中,他们编写了有关苹果和梨的教育示例,然后分5行显示一个程序。在5行示例中,根本不需要任何OOP,因为可以直接将数据传输到“最高管理级别”,而不会出现问题。



当大型项目是“官僚化”的问题时,需要OOP ....

但是回到重点。在实际的OOP中,有SYNTAX糖。上面带有用于结构的Y_all的示例,函数f(,,,)被认为是静态的。当代码开始看起来像这样时,OOP是一组糖:




Y_all.f1(X1); %   Y_all=f1(Y_all, X1), 
Y_all.f2(X2); 
….
Y_all.f20(X20);
Y_all.DO_MOST_IMPORTANT_SHIT(options_how_to_do_this_shit);


那些。我们决定带来一种混乱的语法,在该语法中,您不能编写Y_all 2次,但只能编写1次。重复是口吃之母。



其余的“ OOP如何工作”的解释归结为语法糖的工作原理。



OOP语法糖如何工作



首先,显然必须先创建该数据库Y_all作为函数的参数。这需要一个构造函数。



其次,建议最好事先预见它将具有什么“隔间”。只要Y_all数据库很小,此设置就很烦人。我想梦见“动态创建的类”,就像在MATLAB中一样,可以使用简单的命令Ya = 5Yb = 10来构造结构但是,在调试一个健康的项目之后,幻想有关此主题的愿望就消失了。



下一步-调用方法(函数)。



这就是它的大概演变

功能 评论
Y = f(X) 当我们按点绘制PARABOL时,情况就是如此!
X = f(X) 我们被官僚欺负了,在所有情况下我们只有一批论据,将所有输入和输出数据存储在内部的不同部分中
f(X) 函数为什么要返回参数?这是数学课时代的古老!而且毫无意义的浪费内存!让数据通过引用传递,然后函数本身将到达参数,更改并离开。NOTHING = f(X)

不是山到达穆罕默德,而是穆罕默德到达山。
X.f() 我们只是用句法糖把X的参数拉出来了。无= X.f(无)




现在-内部如何安排这样的函数,该函数需要NOTHING并返回NOTHING(C#中的void关键字)。



我喜欢在matlab中完成此操作(从理解的角度出发):我们称为Xf()的函数在内部编写为

示例MATLAB代码 示例C#代码

function f(this)
    % . 
    this.c=this.a+this.b;
end	



public void f() {
    this.c=this.a+this.b;
}


« » . — ( , this, fuck, shit).

this, .

« » . , ( )!

! , « this». «» this ( ).





这是一个带有“默认参数是this的函数,它位于类中,就像在文件夹中一样-有一个普通方法(xs,因为它正确地用俄语)。

事实上,恶补所有参数到一个单一并不总是正确的。有时您需要其他一些参数(例如,这是用户输入):




public void f(int user_input) {
    this.c=this.a+this.b + user_input;
}


有时,您甚至需要返回一个参数(例如,有关某个操作的成功或失败的信息),而不是编写void但是,这不会更改统计信息:大多数OOP函数都返回NOTHING(void),并且不接受任何内容(默认参数不计算在内)或仅接受很少的参数。



让我们

在MATLAB中编写最终代码




classdef MyClass<handle %  handle      
    properties %   
        a
        b
    end
    methods % 
        function this=MyClass(a, b) %  . a, b -  
            this.a=a
            this.b=b
        end
        function f(this)
            this.c=this.a+this.b
        end
    end
end
%  -  Untitled.m 
X=MyClass(5,10);
X.f();
fprintf(‘X.c=%d',X.c) % .=15


现在在C#中:




public class MyClass {
    public int a;
    public int b;
    public MyClass(int a, int b) { //  . a, b -  ()		
        this.a=a;
        this.b=b;
    }
    public void f(this) {
        this.c=this.a+this.b
    }
}
//  -  
MyClass X=new MyClass(5,10);
X.f();
Console.WriteLine(“X.c={0}”,X.c);  // .=15


当我弄清楚时,似乎编写代码的大多数问题都淡出了背景...



属性与字段



让我们来看一个例子。

没有属性 具有属性

MyClassA{
    int a; // field ()

    public int Get_a(){
        return this.a;
    }    
     
    public void Set_a(int value){ 
    //   - 
    //, ,  value>0
        if (value>0) this.a=value;
        else this.a=0; 
    }
}



MyClassA{
    int a; // field ()

    public int A{
       get{return this.a;}
       set{ 
           if (value>0) 
               this.a=value;
           else 
               this.a=0; 
           }
    }
}



MyClass X=new MyClassA();
X.Set_a(5);
int b=X.Get_a();



MyClass X=new MyClassA();
X.A=5;
int b=X.A;


注释:参数

Set_a可以称为

Set_a(int YourVarName)

评论:

集{...}中的变量应始终称为值



这个东西很方便并且经常使用,但是它仍然是SYNTAX SUGAR。

字段是完全限定的变量。属性是2个类方法(获取和设置),其调用语法复制了“变量调用”。



实际上,在get和set中,您可以执行以下操作:




int A {
    get{ return 0;}
    set{ Console.WriteLine(""); }
}


因此,似乎建议使用大写字母写名称属性,使用小写字母写字段。



它发生了(例如,您不能在接口中创建字段)您需要快速进行属性设置,然后可以:




int A { get; set;} //  , -  _a
// set  get     .
public int B { get; private set;} //    
//(  ,      )


继承,封装,多态



你为什么以前没有提到他们?因为

-实际上,在编写代码时,并不需要查询“ Ok Google,什么是OOP”中提到的那种力量。我什至会说,起初他们实际上是不必要的

-在需要它们的地方,您可以阅读有关它们的信息(只有懒惰者没有写这种情况)。
掌握OOP风格的写作技巧的过程时

-您将拥有大多数没有继承的类。您只需在所需的类中编写所有功能,而您实际上并不需要继承某些东西。

-因此,多态性(继承的洗剂)也

遍及整个森林-“封装”被简化为在任何地方(所有字段,属性和方法)分配公共对象。

这样一来,您的手就会伸到肩膀上,如果没有这篇文章您会自己弄清楚的,在这里您不应该这样做,尤其是在不公开写作的地方。



但仍对其进行简要概述。



遗产。这是一个聪明的复制粘贴



一个有缺陷的“继承”实现看起来像这样:

哦,我的狗屎代码有一个名为MyClass的类,它缺少另一个SHIT字段和另一个DO_THE_SHIT()方法!

* Ctrl + C,Ctrl + V

*创建新类MyClass_s_fichami并在其中添加所需的类

尽管如此,我们还是更加文明的人,我们知道最好不要复制程序的文本,而要引用它。



假设我们仍然使用某种古老的编程语言编写代码,或者没有意识到“继承”之类的东西。然后我们写两个不同的类


public class MyClassA{ 
    public int a;
    public void F1(int x){
    //   
        this.a=this.a*3;
    }
    public MyClassA(int a){ //
        this.a=a;
    }
}



public class MyClassB { 
    //
    private  MyClassA fieldA;
    // get  set     
    // a - .. property
    public int a{ 
        get { return fieldA.a; }
        set { this.fieldA.a=value; }
    }
    public int b;
    //   
    // «»
    public void F1(int x){ 
       this.fieldA.F1();
    }
    public void F2(int x){
        //  
        this.b=this.a*this.b;
    }
    //
    public MyClassB(int a, int b){ 
        this.fieldA= new MyClassA();
        this.a=a;
        this.b=b;
    }
}



//-   
var X=new MyClassA(5);
X.F1(); // X.a   15
Console.WriteLine(X.a); // 15	



//-   
var X=new MyClassB(5,10);
X.F1();// X.a   5*3=15
X.F2();// X.b   15*10=150
Console.WriteLine(X.a); // 15
Console.WriteLine(X.b); // 150




我们在右边所做的就是继承。仅在普通编程语言中,这是通过一个命令完成的:




public class MyClassB : MyClassA { 
    //    MyClassA  , 
    //      base
    
    // a (, , property a)  , 
    //      (.    )
    public int b;
    public void F2(int x){ //  
        this.b=this.a*this.b;
    }
    public MyClassB(int a, int b){ //
    //   base    A 
    //     
        this.a=a;
        this.b=b;
    }
}


该代码以与选项2完全相同的方式在“外部”工作。该对象实际上是一个“嵌套娃娃”-一个对象内部另一个对象愚蠢地坐在那里,并且有“通信通道”,通过这些通道,您可以直接处理内部对象。



扰流板头
image



在matlab中,情况有些有趣。当运行子构造函数MyClassB时,没有对MyClassA祖先构造函数的静默调用



您需要直接创建它。一方面,这很烦人:




classdef MyClassB<MyClassA
    % ... 
    function MyClassB(a, b)
        this@MyClassA(a); %   ,   «»
        this.b=b;
    end
end


但是,如果根本用其他参数(例如MyClassB(d))调用后代,则可以在内部进行转换,例如:




classdef MyClassB<MyClassA
    % ... 
    function MyClassB(d)
        a=d-5;
        this@MyClassA(a); 
        this.b=d+10;
    end
end


在C#中,这不能直接完成,这导致需要编写某种“转换函数”:




class MyClassB:MyClassA{
    //...  
    static int TransformArgs( int d) {return d-5;}
    MyClassB(int d):base(TransformArgs(d)) {this.b=d+10;}
}


或像这样做“静态构造函数”:




class MyClassB:MyClassA {
    //...  
    MyClassB(){} //    
    static MyClassB GetMyClassB(int d) {
        var X=new MyClassB(); //    
        //   
        .a=d-5;
        .b=d+10;
        return X;
    }
}


看来继承,基本上是一切。



自然,没有人会强迫继承者编写“ F1方法a属性,以便将它们必然转换为祖先方法和字段的调用。广播只是默认的“继承”行为。 您可以(当然!这些是另一个类中的其他方法,兄弟)可以这样编写:








public class MyClassB : MyClassA {
    public int a{ //   
        get { return 0; }
        set { base.a=0; }//    this.fieldA.a=0;
    }
    public int b;
    public void F1(int x){ //     «»
        //   - base -  
        Console.WriteLine(“”);//     
    }
}


封装形式



从概念上讲,这意味着在类MyClassB的对象内部,类MyClassA的对象位于基础字段中,并能够在外部广播控制命令。所有这些都写在上面,没有必要重复。



有不同的访问修饰符这样一个话题-公共私有保护......关于他们来说,什么是最有趣的,它是书面到处或多或少正常,我建议只阅读它。

public- 这将意味着field propertymethod从外部是可见的并且可以被拉出。

如果您不知道该怎么办,请写公开信(不好的建议,是的)。



然后找到自己的力量,并在不需要的地方扔掉这个公共的(或者为了清楚起见,用private代替)(进行“重构”)。是的,当然,成为一个有远见的人,在心理学的斗争中行动起来并立即猜测在哪里私有化是非常好的

私有-这意味着文件文件夹对象字段属性方法仅在此类的方法内可见。

但是...这是一个类,而不是INSTANCE(对象)。如果您有类似以下的代码:




class MyClassA{
    private int a=10;
    public void DO_SOMETHING(MyClassA other_obj) { 
    // DO_SOMETHING          
    //  private      MyClassA.
        this.a=100; //    
        other_obj.a=100; //  
    }
}
var X=new MyClassA();
var Y=new MyClassA();
X.DO_SOMETHING(Y);  //  X.a=100, Y.a=100


此类事物用于克隆(有关更多详细信息,请参见其他资源)。



在编写代码时,我试图考虑这种公共私人安排。在对代码进行粗化时,这是一个不可接受的耗时代码。然后事实证明,代码本身需要以根本不同的方式完成。



如果代码是单独编写的,那么提前打扰私人公共是没有意义的,例如还有更重要的任务,实际上是提出并编写了代码……

唯一清楚地说明在哪个地方放置私人和公共的地方是相同的指代某种领域的臭名昭著的属性。




class MyClassA{
    //  private
    private int a; //"private"  C#     .
    //   public
    public int A {get{...;} set{...;}} //   ""
}


在其他地方,为了安排公共场所和私人场所,您需要真正看一下该程序在做什么,并且很可能无法“缺席”学习该程序。

保护-对于派生类的所有方法,这表示“公共”,对于所有其他内容,则表示“专用”。

通常,如果我们假设继承的类只是其祖先的“更复杂的版本”,那是合乎逻辑的。



老实说,我已经忘记了我在哪里明确应用了此保护。通常是公共或私人的。我编写的大多数类都不是从其他任何自定义类继承的,而在这些类中,几乎没有任何此类需求。



给人的印象是,在某些大型项目上工作时需要公共修饰符,这可能需要一群人的支持。只有在长时间坚持一公里长的代码后,才知道在何处应用它们。在“通过通信”学习时,很难以某种方式给予这种理解。



多态性



当我在Matlab中写作时,我根本无法理解为什么需要多态性以及它是什么。

然后,当我切换到C#时,我意识到这是严格的典型语言的功能,并且与OOP的关系非常弱。在matlab中,您可以在任何地方编写代码而无需知道这种多态性的存在-没有严格的类型。



为简单起见,让这些类分别称为AB




class A{...}
class B:A{...}
A X=new B();
//  x  A,   -   B. 
//   .
B x_asB=new B();
A x_asA=(A) x_asB;


这称为类型转换。在C#中,您可以自己(如果知道如何)编写自己的定制的自制类型转换系统,几乎可以将任何类型的类型转换为其他类型。

在这里-开箱即用。由于A的另一个对象位于属于类B的对象x,因此看似明显的转换方式之一是关闭从外部对象到内部对象的所有连接。 这并不是真正必要的,但是发明了“多态性”的人认为这样做是最明显的。用户将自己编写其余选项。 对不起(不再相关)2008-2012年样本的“ politota”。










lass  {...}
class  :  {...} 
  = new Me (); //   
  = () ; //    


接口



我们必须从如何应用此开始。



假设我们有一个列表,我们想在其中添加一些内容。



在matlab中,最简单的方法是(称为单元数组):




myList={1, ‘2’, ‘fuck’, ‘shit’, MyClassA(), MyClassB(), …. ,_, _};


您不认为它是什么类型的对象,只需将其放在列表中即可。



接下来,假设您需要遍历列表,并对每个元素执行以下操作:




for i=1:length(myList)
      item=myList(i);
      %   -   item-
      DoSomeStuff(item);
end


如果DoSomeStuff函数足够聪明,可以消化输入给它的任何内容,则将可以执行此代码。



如果DoSomeStuff函数(或它的作者)没有智能,那么可能会窒息某些东西:数字,直线,您自己制作的类,秃头魔鬼或-上帝禁止-您的祖母。



MATLAB将在控制台中显示英语红色咒骂并终止程序。因此,您的代码会自动获得达尔文奖。



但是,这实际上是不好的,因为有时代码很复杂。然后,您将坚信自己正确地完成了所有操作,但实际上,在测试过程中根本不会启动错误的操作组合。



这就是为什么(虽然不仅是因为)在MATLAB上-我设法确保了我自己(大约在KPDV上),在可怕的代码上确保了-无需编写大型项目。



现在让我们继续C#。我们列出一个清单,然后...,然后要求我们立即指出对象的类型。我们创建一个类型为List的列表。



在这样的列表中,您可以输入数字1。



在这样的列表中,您可以输入数字2,甚至,上帝原谅我,3。




List<int> lst1=new List<int>().
lst.Add(1);
lst.Add(2);
lst.Add(3);


但是文本字符串不再存在。自制课程的对象-绝对不是。我对秃头魔鬼和你的祖母一言不发,无论如何他们都不会在那儿。



您可以制作单独的行列表。您可以-为自己制作的课程。




List<MyClassA> lst2=new List<MyClassA>();
lst2.Add(new MyClassA());


实际上,您可以单独列出您的祖母秃头魔鬼。



但是将它们添加到一个列表中将不起作用。您的代码将赢得达尔文奖,甚至在您尝试运行它之前都会被编译器滥用谨慎地不允许编译器创建DoSomeStuff(item)函数,该函数会“扼杀”其参数。



在大型项目中,这确实非常方便。

但是,当您仍然希望将其放入一个小清单时该怎么办?



这不是真正的问题。将所有内容都转换为type对象就足够了。几乎(甚至绝对)所有内容都可以转换为object类型




List<object> lst=new List<object>();
lst.Add((object) new MyClassA());
lst.Add((object) new MyClassB());


当我们开始遍历列表时,问题就开始了。关键是对象类型(几乎)不能执行任何操作。它只能是object类型的BE

- 你能做什么?

-我会唱歌和跳舞

-我-Sancho ...-Sancho

你能做什么?

我是桑乔

-恩,你能做什么?

- 你不明白。我可以是桑乔。



因此,编写接口。这是您要继承的类。该接口包含方法和属性标头。



在我们的例子中,这些是确保DoSomeStuff(item)函数正常运行的方法和属性。接口本身不实现属性。这是有目的的。实际上,可以仅从适合于DoSomeStuff()函数使用的某个类继承。但这意味着额外的代码和健忘的程序员。



因此,如果同一个程序员继承了一个接口,却忘记了实现类的必需属性和方法,则编译器将用达尔文奖将其写到代码中。因此,您可以执行以下操作:




interface ICanDoTheStuff {...};
class MyClassA: ICanDoTheStuff {…}
class MyClassB: ICanDoTheStuff {…}
static void DoSomeStuff(ICanDoTheStuff item) {…}

List<ICanDoTheStuff> lst= new List<ICanDoTheStuff>();
lst.Add(new MyClassA());
lst.Add(new MyClassB());

for (int i=0; i<lst.Count; i++) {
      ICanDoTheStuff item=myList[i];
      DoSomeStuff(item);
}


那些。为此,最后需要一个接口-为了创建类型列表或类中的某些字段,并绕过在此处添加(到列表或字段)一些剩余垃圾的禁止。



该界面是“官僚制”。尽管是的,但并不是在每个地方都需要它,也不是在所有地方都需要它。



...一般而言,...我为这些苛刻的表达表示歉意,出于某种原因,在我看来,对材料进行“干式”演示是不成功的...



All Articles