SoFunction
Updated on 2025-03-08

Twenty-five questions about basic concepts of C# 16-20

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();
        }
    }
}