0. Why is this update taking so long
- Last Saturday, Sunday, Monday: I dropped my computer in a taxi, and I was already on the train home.
- And, Saturday through Wednesday: a very, very big change.
- Finally, from Thursday to yesterday I had a high fever of 38.8 degrees.
But now that you see this post, the instructions will slowly restore the update speed. I try not to let it affect me.
1. The inheritance
Inheritance itself is a far too common concept for OOP. C# allows only one base class to be inherited. B inherits from A, C inherits from B, and so on.
1.1 Members who will not inherit
Take B inheriting A as an example. Some members of A will not be inherited by B. These members are:
- Static constructors. Used to initialize static data.
- Instance constructor. That’s what we call a constructor. (Although not inherited, can be called)
- Finalizers. It has to do with garbage collection.
1.2 Accessibility of class members
We are only talking about inheritance here.
1.2.1 private member
Even an inherited class cannot access the private members of the base class. There are exceptions, however, where private variables can be accessed by nested inherited classes. B, as in the following code, is accessible, but C is not.
public class A
{
private int value = 3;
public class B : A
{
public int GetValue()
{
return this.value; }}}public class C : A{}class Program
{
static void Main(string[] args)
{
A.B b = new A.B(); // Because B is inside A, it can be accessed through methods.
C c = new C(); // C can't access value even though it inherits from A
Console.WriteLine(b.GetValue()); // Get value}}Copy the code
Even if you put the getValue method in C, you can’t access it.
One of the things that I suddenly realized was that the C# syntax had been confusing me was that there were no special keywords for functions. There was no def, func or anything like that. It’s just int GetValue(), it’s a function with a return value, it’s a class with an int. So if I take the parentheses out, it becomes a variable.
1.2.2 protected members
Compared to private, protected can be accessed by inherited classes. However, the location of the instance code can also affect whether it can be used, as in xia’s example:You can see that Method1() is not accessible because the instantiation is outside of the inherited class, so it has to be accessed indirectly through the code in the comment:
1.2.3 internal members
Problems like the above do not occur in internal and public and can be accessed directly through b.method1 ().
Can be accessed by inherited classes in the same assembly. In different assemblies, the inherited class still cannot access the member.
1. Members of the public
Public can be accessed by inherited classes.
1.3 override and virtual
If you want to override a member of the base class in an inherited class, add the virtual keyword to that member in the base class and the Override keyword to the inherited class. Override reports an error if the virtual keyword is not included. Add only Virtual, not overrride, and there is no problem. Because it only gives the possibility that an inherited class can override it.
1.4 the abstract
The abstract keyword is used if we want the derived class to have to override the base class members.
Abstract is special:
- The class and member must be accompanied by the abstract keyword. Otherwise, an error will be reported.
- Member cannot have implementation. That makes sense, because it’s bound to be overwritten, so it doesn’t make sense to write implementation.
- The abstract class does not mean that all methods are abstract. It is perfectly possible to write virtual methods or ordinary methods that cannot be overridden in an abstract class. The addition of abstract in front of class indicates that the class is abstract and cannot be instantiated, but the methods can be accessed in an inherited class.
1.5 Only classes and interfaces have the concept of inheritance.
Struct, enum, delegate, etc., have no concept of inheritance.
1.6 Implicit inheritance
In fact, all types inherit directly or indirectly from Object. All features and types of Object can be used. Let’s define an empty class:
public class SimpleClass{}Copy the code
When we look at the members of this class through reflection (more on that later), we can see that it contains nine members, one of which is the default constructor, and the other eight are inherited from Object:
- ToString returns a string representation, in this case the class name: “SimpleClass”
- The next three methods all test whether two objects are equal. Typically these methods test whether two variables are referenced equally. That is, the variables to be compared must refer to the same object.
- The GetHashCode method computes a value for the hashed collection
- The GetType method gets a Type Object, in this case of Type SimpleClass. Unlike ToString, this does not return a string, but it does when printed with console. WriteLine.
- Finalize method, for garbage collection.
- MemberwiseClone creates a shallow clone of the current object.
You can override the ToString method in the class definition to override the return value.
2. Design base classes and inheritance classes
Because oop is flexible, and C# provides so many keywords. As a result, design has become a more important part of the process. For example, we have a base class for Publication, and then we have Book, and then Magazine, and so on.
2.1 Design Roadmap
2.1.1 throughout
- There are many things we need to design, such as which members the base class should contain, whether some method members provide implementation, and whether the base class should be an abstract base class.
- One advantage of the non-abstract approach is that you can reuse code. By avoiding writing the same code in multiple inherited classes, you can also avoid creating a lot of bugs.
2.1.2 Number of layers of inheritance relationship
- Oop design is flexible. Take our example. Although you have identified the base class as Publication, you can either derive magazines directly from the Publication or you can derive old Materials first.
- In our example, we are Publication->Book->Magazine.
2.1.3 Instantiating whether to make sense
- If you don’t make sense, use the abstract class instead.
- If it makes sense, instantiate it with the constructor. Of course, you’ll find that you can instantiate without writing a constructor. That’s because the compiler generates a parameterless constructor for you (we covered.ctor in the previous section).
- In our example, since the Publication instantiation does not make sense, we set it to abstract class. However, the abstract method is not included. Abstract classes like this, which have no abstract methods, are generally an abstract concept that is shared by concrete classes (later, Book, etc.).
2.1.4 Whether some members of an inherited class need to override the base class methods
- If so, use virtual and Override together.
2.1.5 Is an inherited class the last level of a hierarchy
Any inherited class can be used as a base class for other classes. The sealed keyword may be used to ensure that the class is the last layer and cannot be used as a base class.
2.2 example – Publication
Directly to the code:
using System;
public enum PublicationType { Misc, Book, Magazine, Article };
public abstract class Publication
{
private bool published = false;
private DateTime datePublished;
private int totalPages;
public Publication(string title, string publisher, PublicationType type)
{
if (String.IsNullOrWhiteSpace(publisher))
throw new ArgumentException("The publisher is required.");
Publisher = publisher;
if (String.IsNullOrWhiteSpace(title))
throw new ArgumentException("The title is required.");
Title = title;
Type = type;
}
public string Publisher { get; }
public string Title { get; }
public PublicationType Type { get; }
public string CopyrightName { get; private set; }
public int CopyrightDate { get; private set; }
public int Pages
{
get { return totalPages; }
set
{
if (value< =0)
throw new ArgumentOutOfRangeException("The number of pages cannot be zero or negative.");
totalPages = value; }}public string GetPublicationDate()
{
if(! published)return "NYP";
else
return datePublished.ToString("d");
}
public void Publish(DateTime datePublished)
{
published = true;
this.datePublished = datePublished;
}
public void Copyright(string copyrightName, int copyrightDate)
{
if (String.IsNullOrWhiteSpace(copyrightName))
throw new ArgumentException("The name of the copyright holder is required.");
CopyrightName = copyrightName;
int currentYear = DateTime.Now.Year;
if (copyrightDate < currentYear - 10 || copyrightDate > currentYear + 2)
throw new ArgumentOutOfRangeException($"The copyright year must be between {currentYear - 10} and {currentYear + 1}");
CopyrightDate = copyrightDate;
}
public override string ToString() => Title;
}
Copy the code
- Why is there a constructor? Of course you can. We just can’t use the constructor to instantiate the Publication instance. But you can use this constructor in inherited classes. This was also mentioned in the last article.
2.3 example Book
using System;
public sealed class Book : Publication
{
public Book(string title, string author, string publisher) :
this(title, String.Empty, author, publisher){}public Book(string title, string isbn, string author, string publisher) : base(title, publisher, PublicationType.Book)
{
// isbn argument must be a 10- or 13-character numeric string without "-" characters.
// We could also determine whether the ISBN is valid by comparing its checksum digit
// with a computed checksum.
//
if (! String.IsNullOrEmpty(isbn)) {
// Determine if ISBN length is correct.
if (! (isbn.Length == 10 | isbn.Length == 13))
throw new ArgumentException("The ISBN must be a 10- or 13-character numeric string.");
ulong nISBN = 0;
if (! UInt64.TryParse(isbn, out nISBN))
throw new ArgumentException("The ISBN can consist of numeric characters only.");
}
ISBN = isbn;
Author = author;
}
public string ISBN { get; }
public string Author { get; }
public Decimal Price { get; private set; }
// A three-digit ISO currency symbol.
public string Currency { get; private set; }
// Returns the old price, and sets a new price.
public Decimal SetPrice(Decimal price, string currency)
{
if (price < 0)
throw new ArgumentOutOfRangeException("The price cannot be negative.");
Decimal oldValue = Price;
Price = price;
if(currency.Length ! =3)
throw new ArgumentException("The ISO currency symbol is a 3-character string.");
Currency = currency;
return oldValue;
}
public override bool Equals(object obj)
{
Book book = obj as Book;
if (book == null)
return false;
else
return ISBN == book.ISBN;
}
public override int GetHashCode() => ISBN.GetHashCode();
public override string ToString()= >$"{(String.IsNullOrEmpty(Author) ? "" : Author + ",")}{Title}";
}
Copy the code
- Two constructors: Use different constructors when the number of arguments is different. You can see that the first constructor calls the second constructor using :this. The second constructor calls the base class constructor. The way a low-argument constructor calls a multi-argument constructor and provides default values is called a constructor chain.
- Not only ToString, but even Equals. Because if not overrriden, Equal tests only for reference equality. You should rewrite GetHashCode as well as Equals. GetHashCode should be consistent with Equals. In this example, GetHashCode also uses ISBN’s method because it compares ISBN numbers.