16. What is the difference between a class and a structure?
answer:
kind:
Classes are reference types allocated on the heap. Assigning instances of the class only copies the references, and all point to the memory allocated by the same actual object.
Classes have constructors and destructors
Classes can be inherited and inherited
structure:
Structure is the value type allocated on the stack (although the stack access speed is faster than the heap, the stack resources are limited), and the assignment of the structure will generate a new object.
Structures do not have constructors, but can be added. Structure has no destructor
Structures cannot be inherited from another structure or are inherited, but they can be inherited from interfaces like classes.
Example:
Based on the above comparison, we can find that some lightweight objects are best structured, but it is best to use classes for logical objects with large data volume or complex processing.
For example: Geoemtry (an introduction in GIS, defined in the OGC standard) is best to use classes, while Geometry is best to use structures among members of the midpoint of Geometry
using System;
using ;
using ;
namespace Example16
{
interface IPoint
{
double X
{
get;
set;
}
double Y
{
get;
set;
}
double Z
{
get;
set;
}
}
//Structure can also be inherited from the interface
struct Point: IPoint
{
private double x, y, z;
//Structure can also add constructors
public Point(double X, double Y, double Z)
{
= X;
= Y;
= Z;
}
public double X
{
get { return x; }
set { x = value; }
}
public double Y
{
get { return x; }
set { x = value; }
}
public double Z
{
get { return x; }
set { x = value; }
}
}
//The design of dot-shaped Geometry has been simplified here, and the actual product also includes complex operations such as Project (coordinate transformation)
class PointGeometry
{
private Point value;
public PointGeometry(double X, double Y, double Z)
{
value = new Point(X, Y, Z);
}
public PointGeometry(Point value)
{
//The assignment of the structure will allocate new memory
= value;
}
public double X
{
get { return ; }
set { = value; }
}
public double Y
{
get { return ; }
set { = value; }
}
public double Z
{
get { return ; }
set { = value; }
}
public static PointGeometry operator +(PointGeometry Left, PointGeometry Rigth)
{
return new PointGeometry( + , + , + );
}
public override string ToString()
{
return ("X: {0}, Y: {1}, Z: {2}", , , );
}
}
class Program
{
static void Main(string[] args)
{
Point tmpPoint = new Point(1, 2, 3);
PointGeometry tmpPG1 = new PointGeometry(tmpPoint);
PointGeometry tmpPG2 = new PointGeometry(tmpPoint);
= 4;
= 5;
= 6;
// Since the structure is a value type, the coordinates of tmpPG1 and tmpPG2 are not the same
(tmpPG1);
(tmpPG2);
// Since the class is a reference type, the coordinate modification of tmpPG1 affects tmpPG3
PointGeometry tmpPG3 = tmpPG1;
= 7;
= 8;
= 9;
(tmpPG1);
(tmpPG3);
();
}
}
}
result:
X: 1, Y: 2, Z: 3
X: 4, Y: 5, Z: 6
X: 7, Y: 8, Z: 9
X: 7, Y: 8, Z: 9
17. What problems will multi-inheritance of interfaces bring?
answer:
The interfaces in C# are different from classes. Multiple inheritance can be used, that is, a child interface can have multiple parent interfaces. But if two parent members have members of the same name, ambiguity will arise (this is also one of the reasons why classes in C# cancel multiple inheritances). It is best to use explicit declarations when implementing this.
Example:
using System;
using ;
using ;
namespace Example17
{
class Program
{
//A complete interface declaration example
interface IExample
{
//Properties
string P
{
get;
set;
}
//method
string F(int Value);
//event
event EventHandler E;
//Index indicator
string this[int Index]
{
get;
set;
}
}
interface IA
{
int Count { get; set;}
}
interface IB
{
int Count();
}
//IC interfaces are inherited from IA and IB multiple times
interface IC : IA, IB
{
}
class C : IC
{
private int count = 100;
//Explanatory declaration to implement the Count property in the IA interface
int
{
get { return 100; }
set { count = value; }
}
//Explanatory declaration of implementing the Count method in the IB interface
int ()
{
return count * count;
}
}
static void Main(string[] args)
{
C tmpObj = new C();
//Explanatory conversion is also required when calling
("Count property: {0}", ((IA)tmpObj).Count);
("Count function: {0}", ((IB)tmpObj).Count());
();
}
}
}
result:
Count property: 100
Count function: 10000
18. What is the difference between abstract classes and interfaces?
answer:
Abstract class can contain function definitions and implementations, and interface can only contain function definitions.
Abstract classes are concepts abstracted from a series of related objects, thus reflecting the internal commonality of things; interfaces are functional conventions defined to satisfy external calls, and therefore reflecting the external characteristics of things.
Analyze objects, extract internal commonalities to form abstract classes, and use them to represent the essence of objects, that is, "what is"
Provide external calls or functions to be expanded to give priority to the use of interfaces.
19. What is the alias indicator?
answer:
Through the alias indicator we can give an alias for a certain type
Mainly used to resolve conflicts with the same name type in two namespaces or to avoid redundant namespaces
The alias indicator is defined at the outermost layer of all namespaces, with scoped as the entire unit file. If the definition is within a namespace, it only works within the namespace that is directly affiliated
Example:
:
using System;
using ;
using ;
namespace .CSharp25QExample.Example19.Lib01
{
class Class1
{
public override string ToString()
{
return ".CSharp25QExample.Example19.Lib01's Class1";
}
}
}
:
using System;
using ;
using ;
namespace .CSharp25QExample.Example19.Lib02
{
class Class1
{
public override string ToString()
{
return ".CSharp25QExample.Example19.Lib02's Class1";
}
}
}
Main unit ():
using System;
using ;
using ;
//Use alias indicator to resolve conflicts of the same name type
//Define at the outermost layer of all namespaces, scoped to the entire unit file
using Lib01Class1 = .CSharp25QExample.Example19.Lib01.Class1;
using Lib02Class2 = .CSharp25QExample.Example19.Lib02.Class1;
namespace Example19
{
namespace Test1
{
//Test1Class1 is defined in the Test1 namespace, and the scope is only within Test1
using Test1Class1 = .CSharp25QExample.Example19.Lib01.Class1;
class Class1
{
//Lib01Class1 and Lib02Class2 can be used normally here
Lib01Class1 tmpObj1 = new Lib01Class1();
Lib02Class2 tmpObj2 = new Lib02Class2();
//TestClass1 can be used normally here
Test1Class1 tmpObj3 = new Test1Class1();
}
}
namespace Test2
{
using Test1Class2 = .CSharp25QExample.Example19.Lib01.Class1;
class Program
{
static void Main(string[] args)
{
//Lib01Class1 and Lib02Class2 can be used normally here
Lib01Class1 tmpObj1 = new Lib01Class1();
Lib02Class2 tmpObj2 = new Lib02Class2();
//Note that TestClass1 cannot be used normally here.
//Because, the alias defined by the Test1 namespace cannot be used in the Test2 namespace
//Test1Class1 tmpObj3 = new Test1Class1();
//TestClass2 can be used normally here
Test1Class2 tmpObj3 = new Test1Class2();
(tmpObj1);
(tmpObj2);
(tmpObj3);
();
}
}
}
}
result:
.CSharp25QExample.Example19.Lib01's Class1
.CSharp25QExample.Example19.Lib02's Class1
.CSharp25QExample.Example19.Lib01's Class1
20. How to manually release resources?
answer:
.NET platform provides GC (Garbage Collection) in memory management, which is responsible for automatically releasing managed resources and memory recycling. However, in the following two situations, we need to manually release resources: 1. Since it cannot release unmanaged resources, we must provide ourselves methods to release unmanaged resources allocated in the object. For example, you use a COM object in the object's implementation code; 2. When your class is running, it will generate a large number of instances (like Geometry in GIS), and you must manually release these resources to improve the operation efficiency of the program
The ideal method is to explicitly provide an interface to the client to manually release the object by manually releasing it. There is an IDisposable interface in the System namespace, which is very suitable to do this, so we can save ourselves from declaring another interface.
Example:
using System;
using ;
using ;
namespace Example20
{
class Program
{
class Class1 : IDisposable
{
//The destructor will become protected void Finalize() after compilation. GC will call this method before recycling the object.
~Class1()
{
Dispose(false);
}
// By implementing this interface, customers can explicitly release objects without waiting for GC to release resources, which is said to reduce efficiency
void ()
{
Dispose(true);
}
//Design the release of unmanaged resources into a virtual function, providing the ability to release the resources of the base class in the inherited class
protected virtual void ReleaseUnmanageResources()
{
//Do something...
}
//Private functions are used to release unmanaged resources
private void Dispose(bool disposing)
{
ReleaseUnmanageResources();
// When true, it means that the client explicitly called the release function. GC needs to notify the object's Finalize method again
// When false, GC must have called the Finalize method of the object, so there is no need to tell GC again that you should not call my Finalize method.
if (disposing)
{
(this);
}
}
}
static void Main(string[] args)
{
//tmpObj1 does not release resources manually, just wait for GC to release it slowly
Class1 tmpObj1 = new Class1();
//tmpObj2 calls the Dispose method, legend is that it is more efficient than waiting for GC to release it
//I personally think it is because I have to view its metadata object by object to confirm whether the Dispose method is implemented
//Of course the most important thing is that we can determine the release time ourselves to save memory and optimize program operation efficiency
Class1 tmpObj2 = new Class1();
((IDisposable)tmpObj2).Dispose();
}
}
}
answer:
kind:
Classes are reference types allocated on the heap. Assigning instances of the class only copies the references, and all point to the memory allocated by the same actual object.
Classes have constructors and destructors
Classes can be inherited and inherited
structure:
Structure is the value type allocated on the stack (although the stack access speed is faster than the heap, the stack resources are limited), and the assignment of the structure will generate a new object.
Structures do not have constructors, but can be added. Structure has no destructor
Structures cannot be inherited from another structure or are inherited, but they can be inherited from interfaces like classes.
Example:
Based on the above comparison, we can find that some lightweight objects are best structured, but it is best to use classes for logical objects with large data volume or complex processing.
For example: Geoemtry (an introduction in GIS, defined in the OGC standard) is best to use classes, while Geometry is best to use structures among members of the midpoint of Geometry
using System;
using ;
using ;
namespace Example16
{
interface IPoint
{
double X
{
get;
set;
}
double Y
{
get;
set;
}
double Z
{
get;
set;
}
}
//Structure can also be inherited from the interface
struct Point: IPoint
{
private double x, y, z;
//Structure can also add constructors
public Point(double X, double Y, double Z)
{
= X;
= Y;
= Z;
}
public double X
{
get { return x; }
set { x = value; }
}
public double Y
{
get { return x; }
set { x = value; }
}
public double Z
{
get { return x; }
set { x = value; }
}
}
//The design of dot-shaped Geometry has been simplified here, and the actual product also includes complex operations such as Project (coordinate transformation)
class PointGeometry
{
private Point value;
public PointGeometry(double X, double Y, double Z)
{
value = new Point(X, Y, Z);
}
public PointGeometry(Point value)
{
//The assignment of the structure will allocate new memory
= value;
}
public double X
{
get { return ; }
set { = value; }
}
public double Y
{
get { return ; }
set { = value; }
}
public double Z
{
get { return ; }
set { = value; }
}
public static PointGeometry operator +(PointGeometry Left, PointGeometry Rigth)
{
return new PointGeometry( + , + , + );
}
public override string ToString()
{
return ("X: {0}, Y: {1}, Z: {2}", , , );
}
}
class Program
{
static void Main(string[] args)
{
Point tmpPoint = new Point(1, 2, 3);
PointGeometry tmpPG1 = new PointGeometry(tmpPoint);
PointGeometry tmpPG2 = new PointGeometry(tmpPoint);
= 4;
= 5;
= 6;
// Since the structure is a value type, the coordinates of tmpPG1 and tmpPG2 are not the same
(tmpPG1);
(tmpPG2);
// Since the class is a reference type, the coordinate modification of tmpPG1 affects tmpPG3
PointGeometry tmpPG3 = tmpPG1;
= 7;
= 8;
= 9;
(tmpPG1);
(tmpPG3);
();
}
}
}
result:
X: 1, Y: 2, Z: 3
X: 4, Y: 5, Z: 6
X: 7, Y: 8, Z: 9
X: 7, Y: 8, Z: 9
17. What problems will multi-inheritance of interfaces bring?
answer:
The interfaces in C# are different from classes. Multiple inheritance can be used, that is, a child interface can have multiple parent interfaces. But if two parent members have members of the same name, ambiguity will arise (this is also one of the reasons why classes in C# cancel multiple inheritances). It is best to use explicit declarations when implementing this.
Example:
using System;
using ;
using ;
namespace Example17
{
class Program
{
//A complete interface declaration example
interface IExample
{
//Properties
string P
{
get;
set;
}
//method
string F(int Value);
//event
event EventHandler E;
//Index indicator
string this[int Index]
{
get;
set;
}
}
interface IA
{
int Count { get; set;}
}
interface IB
{
int Count();
}
//IC interfaces are inherited from IA and IB multiple times
interface IC : IA, IB
{
}
class C : IC
{
private int count = 100;
//Explanatory declaration to implement the Count property in the IA interface
int
{
get { return 100; }
set { count = value; }
}
//Explanatory declaration of implementing the Count method in the IB interface
int ()
{
return count * count;
}
}
static void Main(string[] args)
{
C tmpObj = new C();
//Explanatory conversion is also required when calling
("Count property: {0}", ((IA)tmpObj).Count);
("Count function: {0}", ((IB)tmpObj).Count());
();
}
}
}
result:
Count property: 100
Count function: 10000
18. What is the difference between abstract classes and interfaces?
answer:
Abstract class can contain function definitions and implementations, and interface can only contain function definitions.
Abstract classes are concepts abstracted from a series of related objects, thus reflecting the internal commonality of things; interfaces are functional conventions defined to satisfy external calls, and therefore reflecting the external characteristics of things.
Analyze objects, extract internal commonalities to form abstract classes, and use them to represent the essence of objects, that is, "what is"
Provide external calls or functions to be expanded to give priority to the use of interfaces.
19. What is the alias indicator?
answer:
Through the alias indicator we can give an alias for a certain type
Mainly used to resolve conflicts with the same name type in two namespaces or to avoid redundant namespaces
The alias indicator is defined at the outermost layer of all namespaces, with scoped as the entire unit file. If the definition is within a namespace, it only works within the namespace that is directly affiliated
Example:
:
using System;
using ;
using ;
namespace .CSharp25QExample.Example19.Lib01
{
class Class1
{
public override string ToString()
{
return ".CSharp25QExample.Example19.Lib01's Class1";
}
}
}
:
using System;
using ;
using ;
namespace .CSharp25QExample.Example19.Lib02
{
class Class1
{
public override string ToString()
{
return ".CSharp25QExample.Example19.Lib02's Class1";
}
}
}
Main unit ():
using System;
using ;
using ;
//Use alias indicator to resolve conflicts of the same name type
//Define at the outermost layer of all namespaces, scoped to the entire unit file
using Lib01Class1 = .CSharp25QExample.Example19.Lib01.Class1;
using Lib02Class2 = .CSharp25QExample.Example19.Lib02.Class1;
namespace Example19
{
namespace Test1
{
//Test1Class1 is defined in the Test1 namespace, and the scope is only within Test1
using Test1Class1 = .CSharp25QExample.Example19.Lib01.Class1;
class Class1
{
//Lib01Class1 and Lib02Class2 can be used normally here
Lib01Class1 tmpObj1 = new Lib01Class1();
Lib02Class2 tmpObj2 = new Lib02Class2();
//TestClass1 can be used normally here
Test1Class1 tmpObj3 = new Test1Class1();
}
}
namespace Test2
{
using Test1Class2 = .CSharp25QExample.Example19.Lib01.Class1;
class Program
{
static void Main(string[] args)
{
//Lib01Class1 and Lib02Class2 can be used normally here
Lib01Class1 tmpObj1 = new Lib01Class1();
Lib02Class2 tmpObj2 = new Lib02Class2();
//Note that TestClass1 cannot be used normally here.
//Because, the alias defined by the Test1 namespace cannot be used in the Test2 namespace
//Test1Class1 tmpObj3 = new Test1Class1();
//TestClass2 can be used normally here
Test1Class2 tmpObj3 = new Test1Class2();
(tmpObj1);
(tmpObj2);
(tmpObj3);
();
}
}
}
}
result:
.CSharp25QExample.Example19.Lib01's Class1
.CSharp25QExample.Example19.Lib02's Class1
.CSharp25QExample.Example19.Lib01's Class1
20. How to manually release resources?
answer:
.NET platform provides GC (Garbage Collection) in memory management, which is responsible for automatically releasing managed resources and memory recycling. However, in the following two situations, we need to manually release resources: 1. Since it cannot release unmanaged resources, we must provide ourselves methods to release unmanaged resources allocated in the object. For example, you use a COM object in the object's implementation code; 2. When your class is running, it will generate a large number of instances (like Geometry in GIS), and you must manually release these resources to improve the operation efficiency of the program
The ideal method is to explicitly provide an interface to the client to manually release the object by manually releasing it. There is an IDisposable interface in the System namespace, which is very suitable to do this, so we can save ourselves from declaring another interface.
Example:
using System;
using ;
using ;
namespace Example20
{
class Program
{
class Class1 : IDisposable
{
//The destructor will become protected void Finalize() after compilation. GC will call this method before recycling the object.
~Class1()
{
Dispose(false);
}
// By implementing this interface, customers can explicitly release objects without waiting for GC to release resources, which is said to reduce efficiency
void ()
{
Dispose(true);
}
//Design the release of unmanaged resources into a virtual function, providing the ability to release the resources of the base class in the inherited class
protected virtual void ReleaseUnmanageResources()
{
//Do something...
}
//Private functions are used to release unmanaged resources
private void Dispose(bool disposing)
{
ReleaseUnmanageResources();
// When true, it means that the client explicitly called the release function. GC needs to notify the object's Finalize method again
// When false, GC must have called the Finalize method of the object, so there is no need to tell GC again that you should not call my Finalize method.
if (disposing)
{
(this);
}
}
}
static void Main(string[] args)
{
//tmpObj1 does not release resources manually, just wait for GC to release it slowly
Class1 tmpObj1 = new Class1();
//tmpObj2 calls the Dispose method, legend is that it is more efficient than waiting for GC to release it
//I personally think it is because I have to view its metadata object by object to confirm whether the Dispose method is implemented
//Of course the most important thing is that we can determine the release time ourselves to save memory and optimize program operation efficiency
Class1 tmpObj2 = new Class1();
((IDisposable)tmpObj2).Dispose();
}
}
}