This post was adapted from Unity Connect blogger Shiqiang

Effectie C# thought sharing in Unity

Whenever possible, use properties instead of accessing data members directly

  • Attributes allow data members to be exposed as part of a common interface, while still providing the encapsulation required in an object-oriented environment. Attributes are language elements that you can use as data members, but are still implemented using methods underneath
  • You can easily add checking mechanisms to get and set code segments
  • Because a property is implemented as a method, it has all the language features of a method
  • Property to add multithreading support is very handy. You can enhance the implementation of GET and set accessors to provide synchronization of data access
  • Properties can be defined as virtual.
  • Attributes can be extended to abstract.
  • Generic versions of attribute types can be used.
  • Properties can also be defined as interfaces.
  • Because the implementation access methods get and set are separate methods, after C# 2.0, you can define different access permissions for them to better control the visibility of class members
  • To be consistent with multidimensional arrays, we can create multidimensional indexers that use the same or different types on different dimensions.
  • The data needs to be exposed in the public or protected interface of the type

Favoring runtime constants (readonly) over compile-time constants (const)

const

  • Compile-time constants should be used only when performance is extremely sensitive and the value of constants absolutely does not change from version to version.
  • The value of a const is replaced directly by the value in the object code at compile time
  • Can only be used for numbers and strings

readonly

  • Evaluate at run time. The IL generated by referencing the runtime will refer to the readonly variable, not the value of the variable.
  • Can be any type. Runtime constants must be initialized in a constructor or initializer because they cannot be modified after the constructor executes
  • You can store instance constants with a readonly value that holds a different value for each instance of the class. Compile-time constants are static constants.
  • Sometimes you need a value to be determined at compile time, so it’s better to use run-time constants
  • The value that marks the version number should be a run-time constant because its value changes with each release.
  • The only area where const is superior to readOnly is performance. Using a known constant value is slightly better than accessing a readonly value, but the efficiency gain is minuscule.

conclusion

In summary, we must use const whenever the compiler must get a definite value. Examples include the definition of attributes parameters and enumerations, and values that do not change from release to release. In all other cases, you should choose readonly constants as flexible as possible.

It is recommended to use the IS or AS operators rather than casts

Neither the AS and IS operators perform any user-defined conversions, succeed only if the runtime type matches the target type, and do not create new objects during conversions

is

  • Checks whether an object is compatible with other specified types and returns a Bool, never throwing an exception
  • The AS operator is not valid for value types. In this case, is can be used with a cast.
  • The IS operator should be used only when the conversion cannot be done using AS. Otherwise is is superfluous

as

  • It works the same as a cast, but an exception is never thrown, that is, null is returned if the cast is unsuccessful.
  • As is more secure and efficient than casting
  • Null is returned when the conversion fails, and also when the conversion object is NULL, so when using AS for conversion, simply check whether the reference returned is NULL

Use Conditional attributes instead of #if Conditional compilation

  • Because #if/#endif can be easily abused, it makes writing code harder to understand and harder to debug
  • The conditional feature allows functions to be broken down so that they can only be compiled and made part of a class after some environment variables are defined or a value is set. The most common use of Conditional features is to turn a piece of code into a debug statement.
  • Conditional properties apply only to the whole method
  • Any method that uses the Conditional property can only return void.
  • Conditional properties cannot be applied to blocks of code within a method.
  • Conditional properties cannot be applied to methods that return values
  • A Conditional property method can accept any number of reference type parameters
  • Conditional features generate IL more efficiently than #if/#Eendif
  • Limiting it to the functional level allows for a clearer separation of conditional code to further ensure that the code is well-structured

Understand the relationship between several isotropic judgments

Value types

Override the object.equals () instance method and operatior==() to provide more efficient equivalence judgments

Reference types

The Object.equals () instance method is overridden only if the Object is not considered equal. IEquatable<T> is also implemented when overwriting Equals().

References are equal

If two variables of reference type refer to the same object

Values are equal

Both value types have the same type of variable and contain the same content

public static bool ReferenceEquals (object left, object right)

Check whether the object identities of two different variables are equal. Whether you compare a reference type or a value type, this method is based on the object identity, not the object content.

public static bool Equals (object left, object right)

Used to determine whether the runtime types of two variables are equal.

public virtual bool Equals(object right)

Used for overloading

public static bool operator ==(MyClass left, MyClass right)

Used for overloading

Pay attention to

The object.referenceequals () static method and object.equals () static method should not be overridden

Learn about some of the pits in GetHashCode()

  • Hash based collections that define the hash value of a key include HashSet

    and Dictionary

    containers
    ,v>
  • For reference types, it works fine, but it’s inefficient. For value types, the implementation in the base class is sometimes not even correct.
  • In.net, every Object has a hash code, its value by the System. The Object. GetHashCode () decision
  • Implement your own GetHashCode()
  • If two objects are equal (as defined by Operation ==), then they must generate the same hash code. Otherwise, such hash codes cannot be used to find objects in containers.
  • For any object A, a.gethashcode () must remain unchanged.
  • For all inputs, the hash function should generate hash codes randomly and separately among all integers. So the hash container can get a sufficient efficiency boost

Understand the advantages of a shorter approach

  • C# code translated into executable machine code steps
  • The C# compiler will generate IL and place it in the assembly
  • The JIT will generate machine code for each method (or group of methods, if inline is involved) as needed
  • Shorter methods allow the JIT compiler to better spread the cost of compilation.
  • Shorter methods are also better for inlining
  • Simplify control flow
  • The fewer control branches there are, the easier it will be for the JIT compiler to find variables that are best placed in registers
  • It is not only about the readability of the code, but also about the efficiency of the program

Select variable initializer instead of assignment statement

  • The initializer is executed before all constructors are executed
  • A member initializer is the simplest way to ensure that all members of a type are initialized
  • If all constructors want to initialize a member variable to the same value, you should use an initializer.

Initialize static member variables correctly

Static initializer

  • Special functions (executed before all other methods are executed and before variables or attributes are accessed for the first time)
  • You just need to allocate space for a static member

Static constructor

  • More complex logic to initialize static member variables
  • Catch exception

Use constructor chains (to reduce repeated initialization logic)

example

class Foo{private int id; private string name; public Foo() : this(0,""){}public Foo(int id, string name){this.id = id; this.name = name; }public Foo(int id) : this(id,""){}public Foo(string name) : this(0, name){}}Copy the code

Foo a = new Foo(), b = new Foo(456,"def"), c = new Foo(123), d = new Foo("abc"); SomeType x = new SomeType(), y = new SomeType { Key ="abc" },z = new SomeType { DoB = DateTime.Today };Copy the code

[SeeAlso]

Implement the standard destruction mode

  • If a local variable of a reference type (value type doesn’t matter) is used in a routine that is frequently called, it should be promoted to a member variable
  • Provide static objects for commonly used type instances
  • Creating the final value of an immutable type (such as the string += operator, which creates a new string object and returns it) is not recommended because multiple uses generate a lot of garbage. For simple string operations, string.format is recommended. The StringBuilder class is recommended for complex string operations.)

Distinguish between value types and reference types

Reference types

  • class
  • Defines the behavior of the application
  • Uncertainty about future usage diagrams for types

Value types

  • struct
  • Polymorphism cannot be implemented
  • The best use is to store data
  • The main responsibility lies in data storage
  • The public interfaces of a type are defined by attributes that access its data members
  • This type will never have a derived type
  • This type never needs polymorphic support

Ensure that 0 is a valid state for the value type

Custom enumeration values

Make sure 0 is a valid option

To customize flags

  • 0 is defined as a flag with no state selected (such as None)
  • Enumeration values used as Flags (that is, with the Flags feature added) should always set None to 0

Keep value types constant and atomic

Do not blindly create GET and set accessors for every attribute in a type

Restrict type visibility

emm…. This will mean something

Replace inheritance by defining and implementing interfaces

Abstract base Class

  • Provides a common abstraction for a set of related types
  • Describe what the object is
  • You can provide some concrete implementations for derived classes

Interface

  • A design by contract
  • A type that implements an interface must implement the methods specified in the interface
  • Does it describe how an object will behave
  • Cannot include implementation
  • Cannot contain any specific data members

Understand the difference between interface methods and virtual methods

  • Member methods declared in interfaces are not virtual by default, so derived classes cannot override non-virtual interface members implemented in base classes. To override, declare the interface method virtual
  • A base class can provide a default implementation of a method in an interface, and then a derived class can declare that it implements the interface and inherit the implementation from the base class.
  • Implementing interfaces has more options than creating and overwriting virtual methods. We can create sealed implementations, virtual implementations, or abstract contracts for the class hierarchy. You can also create sealed implementations and provide virtual methods to call in the methods that implement the interface

Implement callbacks with delegates

  • Delegates provide us with type-safe callback definitions. While most common delegate applications are related to events, this is not the only case for C# delegate applications. Delegates are the best choice when there is a need for communication between classes and we expect a looser coupling mechanism than interfaces provide
  • Delegates allow us to configure targets and notify multiple client objects at run time. A delegate object contains the application of a method, which can be static or instance. That is, using delegates, we can communicate with one or more client objects that are associated at run time
  • Callbacks and delegates are so common in C# that C# specifically provides a compact syntax for them in the form of lambda expressions.
  • For historical reasons, delegates in.NET are multicast delegates. During a multicast delegate invocation, each target is called in turn. The delegate object itself does not catch any exceptions. Therefore, any exception thrown by the target ends the invocation of the delegate chain.

Implement notification with the event pattern

  • Events provide a standard mechanism for notifying listeners, and events in C# are a syntactically quick implementation of the observer pattern.
  • An event is a built-in delegate that provides type-safe method signatures for event handlers. Any number of client objects can register their handlers with events and then handle them. These client objects do not need to be presented in the compiler, and events do not need to have subscribers to work.
  • Using events in C# reduces the coupling between the sender and possible notification recipients, and the sender can develop completely independently of the recipient

Avoid putting back references to inner class objects

  • Value types. When client code accesses a value type member through an attribute, it actually returns an object copy of the value type.
  • Constant type. As a System. The String.
  • Define the interface. Restrict customer access to internal data members to a subset of functions.
  • A wrapper. Provide a wrapper that exposes only that wrapper, limiting access to the objects in it.
  • By exposing reference types through public interfaces, users of objects can bypass our defined methods and properties to change the object’s internal structure, which can lead to common errors.

Base class updates are handled only with the new modifier

  • Using the new operator to modify class members redefines non-virtual members that inherit from the base class
  • The new modifier is only used to resolve conflicts between base and derived methods caused by upgrading a base class
  • The new operator must be used with care. If abused at will, the object calls the method of ambiguity.

The original link: connect.unity.com/p/efficient…

Click on the link above to download Unity’s official app, the topic group, online technical q&A, and more dry goods for you to find