乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 来自伊朗阿里巴巴旅游公司的ASP.NET Core知识点和面试题
前言
无意中看到一套来自伊朗阿里巴巴旅游公司的ASP.NET Core知识点和面试题。
注意这个Alibaba Travels Co.和我们中国的阿里巴巴不是一回事。
自我介绍
- 您最近参与的很酷的项目是什么?
- 对于您使用的开发人员工具,您喜欢哪些方面?
- 在开发者社区中,谁激励了你?
- 你有什么宠物项目吗?何等?
- 你昨天/这周学到了什么?
- 你对编码有什么兴奋或兴趣?
- 您最近遇到的技术挑战是什么,您是如何解决的?
- 您能解释一下在前端安全方面解决的任何常用技术或最近解决的问题吗?
- 您个人在最近的项目中采取了哪些措施来提高代码的可维护性?
- 谈谈您的首选开发环境。
- 您熟悉哪些版本控制系统?
- 您能描述一下您在创建网页时的工作流程吗?
- 如果您今年可以掌握一项技术,那会是什么?
- 你是如何处理与老板或合作者的分歧的?
- 您使用哪些资源来了解开发中的最新技术?
知识点
面向对象编程(Object-Oriented Programming)
- Paradigm
- Principles
-
Inheritance
-
Encapsulation and Information Hiding
-
Method Hiding and Overriding
-
Abstraction
- Data Abstraction
- Procedural (Process,Control) Abstraction
- Procedural Abstraction by Parameterization
- Procedural Abstraction by Specification
-
Polymorphism
- Ad hoc Polymorphism
- Overloading Polymorphism
- Coercion Polymorphism
- Universal Polymorphism
- Inclusion Polymorphism
- Parametric polymorphism
- Invariant Parametric polymorphism
- Covariant Parametric polymorphism
- Contravariant Parametric polymorphism
-
Dispatch
- Static Dispatch
- Dynamic Dispatch
- Single dynamic dispatch
- Multiple dynamic dispatch
-
.NET
-
Common Language Runtime
- Intermediate Language (IL)
- Managed language
- Managed code
- Just-In-Time (JIT)
-
Frameworks and Base Class Libraries
- The Base Class Libraries (BCL)
- Application framework layers
- .NET Standard
-
Data Types and Memory Allocation
- Value Types
- Reference Types
- double Versus decimal
- Stack
- Heap
-
Local method
-
CLR behind the scene implementations
-
new versus override
-
Initialization order
-
Boxing and unboxing
-
Struct
-
Ref Structs
-
Friend Assemblies
-
Interface implementations
-
Enum Type-Safety Issues
-
Generics
- Constraints
- Covariance
- Contravariance
-
Events
-
Delegate
- Parameter compatibility
- Return type compatibility
- Generic delegate type
-
Lambda Expressions
- Closures
- Captured variables
- Expression tree
- Lambda Expressions Versus Local Methods
-
try
Statements and Exceptions -
Enumeration and Iterators
-
The
Array
Class -
Nullable<T>
Struct -
Nullable Reference Types
-
Extension Methods
-
Anonymous Types
-
Tuple and ValueTuple
-
Patterns
- Property Patterns
- Tuple Patterns
- Positional Patterns
var
Patterns- Constant Patterns
-
Caller Info Attributes
- CallerMemberName
- CallerFilePath
- CallerLineNumber
-
Dynamic Binding
-
Unsafe Code and Pointers
-
Preprocessor Directives
- Conditional Attributes
-
Comparing Strings
- Equality comparison
- Order comparison
- Ordinal versus culture comparison
-
StringBuilder
-
Text Encodings and Unicode
-
Dates and Times
- TimeSpan
- DateTime and DateTimeOffset
- Time Zones
- Alternatives
-
BigInteger
-
Complex
-
Random
-
The
Guid
Struct -
Standard Equality Protocols
- == and !=
- The virtual object.Equals method
- The static object.Equals method
- The static object.ReferenceEquals method
- The
IEquatable<T>
interface - When Equals and == are not equal
- Overriding GetHashCode
- Overriding Equals
-
Collections
- BitArray
HashSet<T>
andSortedSet<T>
- Dictionaries
EqualityComparer<T>
-
LINQ Query
- Deferred Execution
- Subqueries
- Interpreted Queries
-
Garbage Collection and Memory Consumption
- Finalizers
- How the GC Works
- Managed Memory Leaks
-
Cross-Platform Diagnostics Tools
- dotnet-counters
- dotnet-trace
- dotnet-dump
-
Concurrency and Asynchrony and Threading
- Concurrency vs Multi-Threading vs Async vs Parallelism
- Single-core vs multicore or multiprocessor machine
- Related Data-Structures (ex: ConcurrentDictionary, Channels)
- Sleep
- Yield
- Blocking
- Spinning
- Local versus Shared State
- Passing Data to a Thread
- Exception Handling
- Foreground versus Background Threads
- IAsyncStateMachine
- IAsyncEnumerable
- IAsyncDisposable
- Synchronization Contexts
async void
- The Thread Pool
- Worker Thread vs I/O Thread
- Tasks
- Long-running tasks & Task.Run()
- AggregateException
- awaiter.GetResult() vs .Result
- TaskCompletionSource
- Task.Delay
- Awaiting
- Asynchronous call graph execution
ValueTask<T>
- Precautions
ConfigureAwait(false)
- Cancellation
- Synchronization
- Categories
- Exclusive locking
- lock Statement
- Monitor.Enter and Monitor.Exit
- Choosing the Synchronization Object
- Deadlocks
- Performance
- Mutex
- Locking and Thread Safety
- Nonexclusive locking
- Semaphore and SemaphoreSlim
- Reader/Writer Locks
- Signaling
- AutoResetEvent
- ManualResetEvent
- CountdownEvent
- Barrier Class
- Exclusive locking
- Lazy Initialization
- Thread-Local Storage
- Timers
- SpinLock and SpinWait
- Nonblocking synchronization techniques
- Interlocked
- memory barriers
- volatile
- Monitor.Wait and Monitor.Pulse
- Categories
-
Stream Architecture
- Backing stores
- FileStream
- NetworkStream
- MemoryStream
- PipeStream
- Named pipes
- Anonymous pipes
- Decorators
- BufferedStream
- DeflateStream
- GZipStream
- CryptoStream
- Adapters
- TextReader
- TextWriter
- StreamReader
- StreamWriter
- StringReader
- StringWriter
- Thread Safety
File
andDirectory
class vsFileInfo
andDirectoryInfo
- Memory-Mapped Files
- Backing stores
-
Network Architecture
- URIs
- WebClient
- HttpClient
- HttpListener
- TCP and UDP
-
Serialization Engines
- XmlSerializer
- JsonSerializer
- The data contract serializer
- The binary serializer
-
Assemblies
- The Assembly Manifest
- Resources and Satellite Assemblies
- assembly loading
- Assembly resolution
- Assembly Load Contexts
- AssemblyDependencyResolver
-
Reflection and Metadata
GetType
Method vstypeof
Operator- Obtaining a Type
- Array types
- Nested types
- Type Names
- Instantiating Types
- Member Types
- C# Members versus CLR Members
- Late binding
- Using Delegates for Performance
- Attributes
- Types
- Bit-mapped
- Custom
- Pseudocustom
- Properties and constructor parameters constraints
- Types
-
Dynamic Code Generation
- Generating IL with DynamicMethod
- Emitting Assemblies and Types
-
Dynamic Programming
- Dynamic Language Runtime
- Call Sites
- Dynamic Member Overload Resolution
- Anonymously Calling Members of a Generic Type
ExpandoObject
-
Cryptography
- Hash Algorithms
- Hashing Passwords
- Symmetric Encryption
- Public-Key Encryption and Signing
-
Parallel Programming
- Channel
- Data parallelism and task parallelism
- PLINQ
- The Parallel Class
- Task Parallelism
- Concurrent Collections
BlockingCollection<T>
-
Span<T>
andMemory<T>
- Spans and Slicing
- Forward-Only Enumerators
-
Native and COM Interoperability
- Calling into Native DLLs
- The Purpose of COM
-
Regular Expressions
- Compiled Regular Expressions
- Character Escapes
- Character Sets
- Quantifiers
-
The Roslyn Compiler
- Roslyn Architecture
- Parsing code into syntax trees (the syntactic layer)
- Binding identifiers to symbols (the semantic layer)
- Emitting Intermediate Language (IL)
- Roslyn Architecture
领域驱动设计(DDD)
- Problem-Domain
- Solution-Domain
- Strategic Design
- Ubiquitous Language
- Bounded Context
- Sub-domains
- Tactical Design
- Aggregate Root & Aggregate
- Value-Object
- Domain Services
- Application Services
- Domain Events
- Context Mapping
- Integration between BCs (Messaging, RPC, ...)
- Entity Persistence
- Patterns
- Factory
- Repository
- Unit of Work
- Event Sourcing
- CQRS
Entity Framework
- Change-Tracker
- Inheritance (TPT, TPH)
- Migrations
- Code-First vs Database-First
- Database-Provider mechanisms (SQL-Server, PostgreSQL, SQLite, In-Memory, ...)
- Fluent-API
- SaveChanges(), When & Why?
- Transaction Management
- DbContext Lifetime
- Value-Converters
问题
Async/Concurrency/Threading
- Concurrency vs Multi-Threading vs Async vs Parallelism
- Data-Structures
- ConcurrentDictionary
- ConcurrentQueue
- Channels
- Async & Task Internals
- At least, how many threads are needed to run an async task?
- GetAwaiter().GetResult() vs .Result
- Task vs ValueTask
- Task.WhenAll & Task.WhenAny
- FooAsync.Wait() & FooAsync.WaitAsync()
- Why is
async void
bad? When do we have to use it? - Task.Run
- What does it do?
- Does Task.Run() create a new Thread?!
- Exception-Handling in Fire & Forget scenarios
- Thread.Sleep vs Task.Delay
- Long-running threads (ThreadPool effects, Implementation via TaskFactory)
- ExecutionContext vs SynchronizationContext (aspnetcore breaking changes ?)
- AsyncLocal
- Asynchronous design patterns like AsyncLazy
- CancellationToken
- Usages
- Creating a cancellation-token using CancellationTokenSource
- Canceling a CPU-Bound Task
- How can we timeout an async task after some time using CancellationTokens?
- IAsyncStateMachine
- What's that?
- How does it work internally?
- IAsyncEnumerable
- IAsyncDisposable
- What's that?
- Where do we need that?
- Locking
- Ways
lock
keyword- Monitor.Enter
- Semaphore
- Semaphore-Slim
- Strategies
- Optimistic
- Pessimistic
- Ways
- ThreadPool
- What's thread-pool?
- Why should we use that?
- How can we run a piece of code on a thread-pool thread? (ThreadPool.QueueUserWorkItem)
DDD
- What's Problem-Domain?
- What's Solution-Domain?
- Strategic Design
- What's Ubiquitous Language?
- What's Bounded Context?
- What are DDD sub-domains?
- What's Core domain?
- What's Generic domain?
- What's Supporting domain?
- Tactical Design
- What's Aggregate?
- What's Value-Object?
- Can we use C# 9 records as Value-Objects?
- Ask for some examples of Value-Objects
- How can we persist Value-Objects?
- Domain Services
- What's a domain-service?
- How it's different from application-services?
- Domain Events
- What is domain-event?
- Where we use them?
- How to raise them?
- Context Mapping
- What is context-mapping?
- What are the different context-mapping patterns?
- Open/Host Service
- Conformist
- Anticorruption-Layer
- Shared-Kernel
- Customer/Supplier
- Partnership
- Published Language
- Separate Ways
- What's Upstream and Downstream?
- Integration between BCs (messaging, RPC, ...)
- Entity Persistence
- What's persistence-ignorance?
- Patterns
- Factories
- Repositories
- What's repository?
- What's unit-of-work?
- Generic repository or Explicit repositories?
- Event Sourcing
- What's event-sourcing?
- What are streams in event-sourcing?
- When should we delete events from the stream?
- CQRS
- What's CQRS?
- When should we do that?
- How to synchronize Command and Query databases?
.NET
-
Common Language Runtime
- Provides essential runtime services such as automatic memory management and exception handling
- The word common refers to the fact that the same runtime can be shared by other managed
programming languages, such as F#, Visual Basic, and Managed C++ - C# is called a managed language because it compiles source code into managed code,
which is represented in Intermediate Language (IL) - The CLR converts the IL into the native code of the machine, such as X86 or X64,
usually just prior to execution. This is referred to as Just-In-Time (JIT) compilation - The container for managed code is called an assembly. An assembly contains not only IL,
but type information (metadata)
-
Frameworks and Base Class Libraries
-
The Base Class Libraries (BCL) sit atop the CLR, providing features useful to any kind of application
(such as collections, XML/JSON, input/output [I/O], networking, serialization, and parallel programming) -
Sitting atop the BCL are application framework layers, which provide the APIs for a user interface
paradigm (such as ASP.NET Core for a web application, or Windows Presentation Foundation
[WPF] for a rich-client application) -
.NET Standard is not a framework; it’s merely a specification describing a minimum baseline of functionality
(types and members) that guarantees compatibility with a certain set of frameworks. The concept is similar
to C# interfaces: .NET Standard is like an interface that concrete types (frameworks) can implement.- Reference Assemblies
-
-
Value Types
- All numeric types, the char type, and the bool type as well as custom struct and enum types
- The content of a value-type variable or constant is simply a value
- The assignment of a value-type instance always copies the instance
- Value-type instances occupy precisely the memory required to store their fields
- Value-type instances (and object references) live wherever the variable was declared. If the instance
was declared as a field within a class type, or as an array element, that instance lives on the heap and
if it appears as a parameter or local variable, it will reside on the stack
-
Reference Types
- All class, array, delegate, and interface types and string type
- Having two parts
- An object
- The reference to that object
- Assigning a reference-type variable copies the reference, not the object instance
- Require separate allocations of memory for the reference and object. The object consumes as many bytes
as its fields, plus additional administrative overhead. The precise overhead is intrinsically private to
the implementation of the .NET runtime, but at minimum, the overhead is eight bytes, used to store a key
to the object’s type as well as temporary information such as its lock state for multithreading and a flag
to indicate whether it has been fixed from movement by the garbage collector. Each reference to an object
requires an extra four or eight bytes, depending on whether the .NET runtime is running on a 32- or 64-bit
platform
-
double Versus decimal
- double is useful for scientific computations (such as computing spatial coordinates)
- float and double internally represent numbers in base 2. For this reason, only numbers expressible
in base 2 are represented precisely
- float and double internally represent numbers in base 2. For this reason, only numbers expressible
- decimal is useful for financial computations and values that are man-made rather than the result of
real-world measurements- decimal works in base 10
- double is useful for scientific computations (such as computing spatial coordinates)
-
Although a Boolean value requires only one bit of storage, the runtime will use one byte of memory because
this is the minimum chunk that the runtime and processor can efficiently work with. To avoid space
inefficiency in the case of arrays, .NET provides a BitArray class in the System.Collections namespace
that is designed to use just one bit per Boolean value -
C#’s char type (aliasing the System.Char type) represents a Unicode character and occupies 2 bytes (UTF-16)
-
The elements in an array are always stored in a contiguous block of memory, providing highly efficient access
-
Stack
- The stack is a block of memory for storing local variables and parameters. The stack logically grows and
shrinks as a method or function is entered and exited
- The stack is a block of memory for storing local variables and parameters. The stack logically grows and
-
Heap
- The heap is the memory in which objects (i.e., reference-type instances) reside
- The heap also stores static fields
-
The in modifier
- An in parameter is similar to a ref parameter except that the argument’s value cannot modified by the
method (doing so generates a compile-time error). This modifier is most useful when passing a large value
type to the method because it allows the compiler to avoid the overhead of copying the argument prior to
passing it in while still protecting the original value from modification
- An in parameter is similar to a ref parameter except that the argument’s value cannot modified by the
-
Local method
- Is visible only to the enclosing method
- A benefit of local methods is that they can access the local variables and parameters of the enclosing method
- Can appear within other function kinds, such as property accessors, constructors, or inside other local
methods, and inside lambda expressions that use a statement block. Local methods can be iterators
or asynchronous
-
CLR property implementation
- C# property accessors internally compile to methods called get_XXX and set_XXX
-
CLR indexer implementation
- Indexers internally compile to methods called get_Item and set_Item
-
new versus override
public class BaseClass { public virtual void Foo() { Console.WriteLine ("BaseClass.Foo"); } } public class Overrider : BaseClass { public override void Foo() { Console.WriteLine ("Overrider.Foo"); } } public class Hider : BaseClass { public new void Foo() { Console.WriteLine ("Hider.Foo"); } } Overrider over = new Overrider(); BaseClass b1 = over; over.Foo(); // Overrider.Foo b1.Foo(); // Overrider.Foo Hider h = new Hider(); BaseClass b2 = h; h.Foo(); // Hider.Foo b2.Foo(); // BaseClass.Foo
-
Constructor and field initialization order
- From subclass to base class
- Fields are initialized
- Arguments to base-class constructor calls are evaluated
- From base class to subclass
- Constructor bodies execute
- From subclass to base class
-
Copying semantics of boxing and unboxing
-
Boxing copies the value-type instance into the new object, and unboxing copies the contents of the object
back into a value-type instance. In the following example, changing the value of i doesn’t change
its previously boxed copy:int i = 3; object boxed = i; i = 5; Console.WriteLine (boxed); // 3
-
-
GetType Method vs typeof Operator
-
Struct
-
A struct is a value type, whereas a class is a reference type
-
A struct does not support inheritance (other than implicitly deriving from object, or more precisely,
System.ValueType) -
A struct is appropriate when value-type semantics are desirable
-
A struct can have all of the members that a class can, except the following
- A parameterless constructor
- Field initializers
- A finalizer
- Virtual or protected members
-
Converting a struct to an interface causes boxing. Calling an implicitly implemented member on a struct
does not cause boxing:interface I { void Foo(); } struct S : I { public void Foo() {} } ... S s = new S(); s.Foo(); // No boxing. I i = s; // Box occurs when casting to interface. i.Foo();
-
Ref Structs
- To ensure that it can only ever reside on the stack
- Introduced mainly for the benefit of the
Span<T>
and ReadOnlySpan<T>
structs - Ref structs cannot partake in any C# feature that directly or indirectly introduces the possibility
of existing on the heap- Lambda expressions
- iterators
- asynchronous functions
- Cannot appear inside non-ref structs
- Cannot implement interfaces (because this could result in boxing)
-
-
Friend Assemblies
- Expose internal members to other friend assemblies
-
Reimplementing an Interface in a Subclass
public interface IUndoable { void Undo(); } public class TextBox : IUndoable { void IUndoable.Undo() => Console.WriteLine ("TextBox.Undo"); } public class RichTextBox : TextBox, IUndoable { public void Undo() => Console.WriteLine ("RichTextBox.Undo"); } RichTextBox r = new RichTextBox(); r.Undo();// RichTextBox.Undo ((IUndoable)r).Undo(); // RichTextBox.Undo
Assuming the same RichTextBox definition, suppose that TextBox implemented Undo implicitly:
public class TextBox : IUndoable { public void Undo() => Console.WriteLine ("TextBox.Undo"); } RichTextBox r = new RichTextBox(); r.Undo();// RichTextBox.Undo ((IUndoable)r).Undo();// RichTextBox.Undo ((TextBox)r).Undo();// TextBox.Undo
-
Enum Type-Safety Issues
- Use
Enum.IsDefined
- Use
-
Generic
-
Avoid casting and boxing
-
Open generic types do not exist at runtime: open generic types are closed as part of compilation
(see also Generics in the Run Time) -
Static data is unique for each closed type
-
Constraints
where T : base-class // Base-class constraint where T : interface // Interface constraint where T : class // Reference-type constraint where T : class? // Nullable Reference-type constraint where T : struct // Value-type constraint (excludes Nullable types) where T : unmanaged // Unmanaged constraint where T : new() // Parameterless constructor constraint where U : T // Naked type constraint where T : notnull // Non-nullable value type, or from C# 8 // a non-nullable reference type.
-
Covariance
-
Assuming A is convertible to B, X has a covariant type parameter if
X<A>
is convertible toX<B>
(convertible via an implicit reference conversion— such as A subclassing B, or A implementing B) -
For instance, type
IFoo<T>
has a covariant T if the following is legal:IFoo<string> s = ...; IFoo<object> b = s;
-
Interfaces and delegates permit covariant type parameters, but classes do not because a class can implement
both Covariance and Contravariance interfaces -
B[] can be cast to A[] if B subclasses A (and both are reference types)
Bear[] bears = new Bear[3]; Animal[] animals = bears; // OK
The downside of this reusability is that element assignments can fail at runtime:
animals[0] = new Camel(); // Runtime error
-
Declaring a covariant type parameter
-
Type parameters on interfaces and delegates can be declared covariant by marking them with
theout
modifier -
Ensures that, unlike with arrays, covariant type parameters are fully type-safe
-
The out modifier on
T
indicates thatT
is used only in output positions
(e.g., return types for methods) -
The compiler will generate an error if you use a covariant type parameter in an input position
(e.g., a parameter to a method or a writable property)public interface IPoppable<out T> { T Pop(); } var bears = new Stack<Bear>(); bears.Push (new Bear()); // Bears implements IPoppable<Bear>. We can convert to IPoppable<Animal>: IPoppable<Animal> animals = bears; // Legal Animal a = animals.Pop();
-
-
-
Contravariance
-
assuming that A allows an implicit reference conversion to B, Contravariance is when you can convert
in the reverse direction—fromX<B>
toX<A>
-
Is supported if the type parameter appears only in input positions and is designated with the
in
modifierpublic interface IPushable<in T> { void Push (T obj); } IPushable<Animal> animals = new Stack<Animal>(); IPushable<Bear> bears = animals; // Legal bears.Push (new Bear());
-
-
-
Delegate
-
Parameter compatibility
When you call a method, you can supply arguments that have more specific types than the parameters
of that method. This is ordinary polymorphic behavior. For the same reason, a delegate can have more
specific parameter types than its method target. This is called Contravariance:delegate void StringAction(string s); class Test { static void Main() { StringAction sa = new StringAction(ActOnObject); sa("hello"); } static void ActOnObject(object o) => Console.WriteLine(o); // hello }
-
Return type compatibility
If you call a method, you might get back a type that is more specific than what you asked for.
This is ordinary polymorphic behavior. For the same reason, a delegate’s target method might return
a more specific type than described by the delegate. This is called Covariance:delegate object ObjectRetriever(); class Test { static void Main() { ObjectRetriever o = new ObjectRetriever(RetrieveString); object result = o(); Console.WriteLine(result); // hello } static string RetrieveString() => "hello"; }
-
Generic delegate type
delegate TResult Func<out TResult>(); // allowing: Func<string> x = ...; Func<object> y = x; delegate void Action<in T> (T arg); // allowing: Action<object> x = ...; Action<string> y = x;
-
-
Events
-
Three things happen under the hood when you declare an event as follows:
public class Broadcaster { public event PriceChangedHandler PriceChanged; }
-
First, the compiler translates the event declaration into something close to the following:
PriceChangedHandler priceChanged; // private delegate public event PriceChangedHandler PriceChanged { add { priceChanged += value; } remove { priceChanged -= value; } }
-
Second, the compiler looks within the Broadcaster class for references to PriceChanged that perform operations
other than += or -=, and redirects them to the underlying priceChanged delegate field. -
Third, the compiler translates += and -= operations on the event to calls to the event’s add and remove
accessors.
-
-
If remove the event keyword and use PriceChanged as a delegate, subscribers could do the following
- Replace other subscribers by reassigning PriceChanged (instead of using the += operator).
- Clear all subscribers (by setting PriceChanged to null).
- Broadcast to other subscribers by invoking the delegate.
-
Standard Event Pattern
-
-
Lambda Expressions
-
The compiler immediately converts the lambda expression to either of the following:
- A delegate instance
- An expression tree, of type
Expression<TDelegate>
, representing the code inside the lambda expression
in a traversable object model. This allows the lambda expression to be interpreted later at runtime
-
Internally, the compiler resolves lambda expressions of this type by writing a private method and then
moving the expression’s code into that method:delegate int Transformer (int i); Transformer sqr = x => x * x; Console.WriteLine (sqr(3)); // 9
-
A lambda expression that captures variables is called a Closure
-
Outer variables referenced by a lambda expression are called captured variables
-
Captured variables are evaluated when the delegate is actually invoked,
not when the variables were capturedint factor = 2; Func<int, int> multiplier = n => n * factor; factor = 10; Console.WriteLine(multiplier(3)); // 30
-
Lambda expressions can themselves update captured variables
int seed = 0; Func<int> natural = () => seed++; Console.WriteLine(natural());// 0 Console.WriteLine(natural());// 1 Console.WriteLine(seed);// 2
-
A local variable instantiated within a lambda expression is unique per invocation of the delegate instance
-
Capturing is internally implemented by “hoisting” the captured variables into fields of a private class.
When the method is called, the class is instantiated and lifetime-bound to the delegate instance -
Capturing iteration variables
-
Lambda Expressions Versus Local Methods
-
Anonymous and local methods capture outer variables in the same way lambda expressions do
-
-
try Statements and Exceptions
- Checking for preventable errors is preferable to relying on try/catch blocks because exceptions are
relatively expensive to handle, taking hundreds of clock cycles or more - Exception filters
- The finally Block
- The using statement
- If we replaced throw with throw ex, the example would still work, but the StackTrace property of the
newly propagated exception would no longer reflect the original error.
- Checking for preventable errors is preferable to relying on try/catch blocks because exceptions are
-
Enumeration and Iterators
- The compiler converts iterator methods into private classes that implement
IEnumerable<T>
and/orIEnumerator<T>
. The logic within the iterator block is “inverted” and spliced into the MoveNext
method and Current property on the compilerwritten enumerator class. This means that when you call an
iterator method, all you’re doing is instantiating the compilerwritten class; none of your code actually
runs! Your code runs only when you start enumerating over the resultant sequence, typically with a
foreach statement. yield break
andyield return
- A
yield return
statement cannot appear in a try block that has a catch clause Nor can yield return appear
in a catch or finally block.- These restrictions are due to the fact that the compiler must translate iterators into ordinary classes
with MoveNext, Current, and Dispose members, and translating exception handling blocks would create
excessive complexity
- These restrictions are due to the fact that the compiler must translate iterators into ordinary classes
- The compiler converts iterator methods into private classes that implement
-
The Array Class
-
Nullable<T>
Struct- Operator Lifting
- Equality operators
- Relational operators
- Operator Lifting
-
Nullable Reference Types
-
Extension Methods
-
Anonymous Types
-
Tuple and ValueTuple
-
Patterns
- Property Patterns
- Tuple Patterns
- Positional Patterns
var
Patterns- Constant Patterns
-
Caller Info Attributes
- CallerMemberName
- CallerFilePath
- CallerLineNumber
-
Dynamic Binding
-
Unsafe Code and Pointers
- Pointer types are primarily useful for interoperability with C APIs, but you also can use them for
accessing memory outside the managed heap or for performance-critical hotspots - The
stackalloc
Keyword
- Pointer types are primarily useful for interoperability with C APIs, but you also can use them for
-
Preprocessor Directives
- Conditional Attributes
-
Comparing Strings
- Equality comparison
- order comparison
- Ordinal versus culture comparison
-
StringBuilder
-
Text Encodings and Unicode
-
Dates and Times
- TimeSpan
- DateTime and DateTimeOffset
- Time Zones
- Alternatives
-
BigInteger
-
Complex
-
Random
-
The Guid Struct
-
Standard Equality Protocols
- == and !=
- The virtual object.Equals method
- The static object.Equals method
- The static object.ReferenceEquals method
- The
IEquatable<T>
interface - When Equals and == are not equal
- Overriding GetHashCode
- Overriding Equals
-
Collections
- BitArray
HashSet<T>
andSortedSet<T>
- Dictionaries
EqualityComparer<T>
-
LINQ Query
- The compiler processes a Query Expression by translating it into fluent syntax. It does this in a fairly
mechanical fashion—much like it translates foreach statements into calls to GetEnumerator and MoveNext.
This means that anything you can write in query syntax you can also write in fluent syntax - Deferred Execution
- How Deferred Execution Works
- Captured Variables
- Subqueries
- Subqueries and Deferred Execution
- Interpreted Queries
- The compiler processes a Query Expression by translating it into fluent syntax. It does this in a fairly
-
Expression Trees
- The Expression DOM
-
Garbage Collection and Memory Consumption
- Finalizers
- How the GC Works
- Generational collection
- The Large Object Heap
- Workstation versus server collection
- Background collection
- Memory Pressure
- Array Pooling
- Sync vs Async resource release (await using vs using) eg: DisposeAsync
- Managed Memory Leaks
- Timers
- Event handlers and Weak References
-
Cross-Platform Diagnostics Tools
- dotnet-counters
- dotnet-trace
- dotnet-dump
-
Concurrency and Asynchrony
- On a single-core computer, the operating system must allocate “slices” of time to each thread
(typically 20 ms in Windows) to simulate concurrency, resulting in repeated blocks of x and y.
On a multicore or multiprocessor machine, the two threads can genuinely execute in parallel
(subject to competition by other active processes on the computer), although you still get
repeated blocks of x and y in this example because of subtleties in the mechanism by which
Console handles concurrent requests - Thread.Sleep(0) relinquishes the thread’s current time slice immediately, voluntarily handing over the
CPU to other threads. Thread.Yield() does the same thing except that it relinquishes only to threads
running on the same processor. - Blocking
- I/O-bound versus compute-bound
- Blocking versus spinning
- Local versus Shared State
- Passing Data to a Thread
- Lambda expressions and captured variables
- Exception Handling
- Centralized exception handling
- Foreground versus Background Threads
- Signaling
- Synchronization Contexts
- The Thread Pool
- Hygiene in the thread pool
- Tasks
- Long-running tasks
- The CLR wraps the exception in an AggregateException in order to play well with parallel programming
scenarios - awaiter.GetResult() vs .Result
- TaskCompletionSource
- Task.Delay is the asynchronous equivalent of Thread.Sleep
- Awaiting
- Upon encountering an await expression, execution (normally) returns to the caller—rather like with yield
return in an iterator. But before returning, the runtime attaches a continuation to the awaited task,
ensuring that when the task completes, execution jumps back into the method and continues where it left
off. If the task faults, its exception is rethrown, otherwise its return value is assigned to the
await expression - Capturing local state
- Upon encountering an await expression, execution (normally) returns to the caller—rather like with yield
- Asynchronous call graph execution
ValueTask<T>
- Precautions
ConfigureAwait(false)
- Cancellation
- On a single-core computer, the operating system must allocate “slices” of time to each thread
-
Stream Architecture
- Backing stores
- Decorators
- Adapters
- PipeStream
- Named pipes
Entity Framework
- Change-Tracker
- What's change-tracker?
- What's .AsNoTracking()?
- How EF detect changes when you update a property value?
- Inheritance (TPT, TPH)
- What's TPT?
- What's TPH?
- Migrations
- What are migrations?
- What we achieve with them?
- Code-First vs Database-First
- What's code-first approach?
- What's database-first approach?
- How to use code-first when we have an existing database schema?
- Database-Provider mechanisms (SQL-Server, PostgreSQL, SQLite, In-Memory, ...)
- What are Providers in EF-Core?
- How can we use EF-Core in unit-tests?
- Fluent-API
- What's fluent-api?
- Advantages and downsides against Attributes
- SaveChanges(), When & Why?
- Transaction Management
- How can we rollback a failed transaction in EF?
- DbContext Lifetime
- What's EF-Core's default lifetime? Do you know Why?
- Can we use Singleton as DbContext's lifetime? Why?
- Value-Converters
- What are value-converters in EF?
- What are its usages?
Object-Oriented
Programming paradigms
- What are programming paradigms?
- A programming paradigm is a way of conceptualizing what it means to perform computation, and how tasks that are to be carried out on a computer should be structured and organized.
- Programming paradigm involves viewing the solution of a problem by combining Data and Algorithms in different ways
OOP Paradigms
- What's OOP paradigms
- A very powerful paradigm for modeling real-world phenomena in a computational model
Object-Oriented
- What Is a Class?
- What Is an Abstract Class?
- What Is an Interface?
- What Is an Object?
- What is the difference between a class and an object?
- What are the differences between an abstract class and an interface?
- What are access specifiers?
- What is Static Class?
- What is the Static Method?
- What are Object Oriented Principles
Object Oriented Principles
- What are Abstraction, Encapsulation, and Information Hiding differences?
- Abstraction is only concerned about which item should be hidden. What should (not) be hidden (or ignored) is the concern of abstraction.
- Encapsulation is bundling of data and operations on the data into an entity called a class and is not concerned with whether the items that are bundled in an entity are hidden from other modules in the application or not.
- Information Hiding is concerned with how an item is hidden
Abstraction
- What's Abstraction?
- Abstraction is a way to perform decomposition of a problem by focusing on relevant details and ignoring the irrelevant details about it in a particular context
- what's decomposition?
- involves analyzing a complex problem or system and breaking it down into smaller parts that are more manageable and easy to understand
- What types of Abstraction are there?
Data Abstraction
- What's Data Abstraction?
- hiding the details about the data
- lets programmers create a new data type called an abstract data type (ADT)
- How can achieve data abstraction?
Procedural (Process,Control) Abstraction
- What's Procedural Abstraction?
- hide the internal implementation and details
- What types of Procedural Abstraction exists?
Procedural Abstraction by Parameterization
- What is Procedural Abstraction by Parameterization?
- We seek generality by allowing the same mechanism to be adapted to many different contexts by providing it with information on that context
Procedural Abstraction by Specification
- What is Procedural Abstraction by Specification?
- We ignore the implementation details and agree to treat as acceptable any implementation that adheres to the specification
Encapsulation and Information Hiding
- What is Encapsulation?
- Encapsulation is simply the bundling of items together into one entity
- What is Information Hiding?
- Information hiding is the process of hiding implementation details that are likely to change
Inheritance
- What's Inheritance?
- The inheritance mechanism lets you define a new abstraction by extending an existing abstraction
- Inheritance allows you to use varying degrees of abstraction at different levels of hierarchy
- Inheritance is also used as a technique to implement polymorphism
- What's Multiple Inheritance?
- Name ambiguity
- Inherited, different features can have the same name
- Same features may be inherited several times
- Impact on substitutability
- Parent constructor calling in Diamond problem
- Ambiguity in calling method has override in supper classes but not in descendent class
- Overriding a method that has been inherited from several supper classes
- Increase Complexity
- Name ambiguity
Polymorphism
- What's Polymorphism?
- Polymorphism is the ability of an entity (e.g. variable, class, method, object, code, parameter, etc.) to take on different meanings in different contexts.
- What types of Polymorphism are there?
Ad hoc Polymorphism
- What's Ad hoc Polymorphism?
- a piece of code works finite and all those types must be known when the code is written
- What types of Ad hoc Polymorphism are there?
Overloading Polymorphism
- What's Overloading Polymorphism?
- When a method or an operator has at least two definitions that work on a different type
- Make an example
//Method overloading Do() Do(10) //operator overloading var i = 1 + 1; var j = 1.5 + 2;
Coercion Polymorphism
-
What's Coercion Polymorphism?
- When a type is implicitly converted (coerced) to another type automatically even if it was not intended explicitly
-
Make an example
var int i = 1; var double d = i; var double d = (double)i;
Universal Polymorphism
- What's Universal Polymorphism?
- a piece of code is written in such a way that it works for an infinite number of types
- What types of Universal Polymorphism are there?
Inclusion Polymorphism
-
What's Inclusion Polymorphism?
- When a piece of code that is written using a type works for all its subtypes
-
Make an example
void processDetails(Person p) { // Write code using the formal parameter p, which is of type Person. // The same code will work if an object of any of the subclass of Person is passed to this method. } /************************************client code*************************************************/ Person p1 = new Person(); Employee e1 = new ; Customer c1 = create a Customer object; processDetails(p1); // Use Person type processDetails(e1); // Use Employee type, which is a subclass of Person processDetails(c1); // Use Customer type, which is a subclass of Person
Parametric polymorphism
- What's Parametric polymorphism?
- Parametric polymorphism is achieved by using a type variable when writing the code, rather than using any specific type
- It is also called “true” polymorphism because it lets you write true generic code that works for any types (related or unrelated)
- What types of Parametric Polymorphism are there?
- Invariant
- Means that you can use only the type originally specified
- Covariant
- Enables you to use a more derived type than originally specified
- Contravariant
- Enables you to use a more generic (less derived) type than originally specified
- Invariant
Invariant Parametric polymorphism
-
What's Invariant Parametric polymorphism?
- Means that you can use only the type originally specified
var list = new Lis<int>();
Covariant Parametric polymorphism
-
What's Covariant Parametric polymorphism?
- Enables you to use a more derived type than originally specified
IEnumerable<Derived> enumerable1 = new List<Derived>(); // IEnumerable<out T> IEnumerable<Base> enumerable2 = enumerable1;
Contravariant Parametric polymorphism
-
What's Contravariant Parametric polymorphism?
- Enables you to use a more generic (less derived) type than originally specified
Action<Base> action1 = target => { ... }; //Action<in T> Action<Derived> action2 = action2; action2(new Derived());
Dispatch
- What's Dispatching?
- it is a way how the programming language calls a method or a function
- What types of Dispatch are there?
- Static Dispatch
- Every method is known at the compile time
- Dynamic Dispatch
- Dynamically dispatched methods are determined at run time based on its parameter’s types
- Static Dispatch
Static Dispatch
- What's Static Dispatch?
- Every method is known at the compile time
- Make an example
Output://classes interface IBar {} class Bar : IBar {} sealed class FooBar : Bar {} //methods void Print(IBar item){Console.WriteLine("It is an IBar.");} void Print(Bar item){Console.WriteLine("It is a Bar.");} void Print(FooBar item){Console.WriteLine("It is a FooBar.");} //call methods var bar = new Bar(); var foo = new FooBar(); IBar ibar = new FooBar(); Print(bar); Print(foo); Print(ibar);
It is a Bar. It is a FooBar. It is an IBar.
Dynamic Dispatch
- What's Dynamic Dispatch?
- Dynamically dispatched methods are determined at run time based on their parameter’s types
- What types of Dynamic Dispatch are there?
- Single dynamic dispatch
- Single ones use just one parameter to select a method
- Satisfy by overriding
- Multiple dynamic Dispatch
- The multiple ones can take advantage of as many parameters they want
- Satisfy by DLR (Dynamic Language Runtime)
- Single dynamic dispatch
Single dynamic dispatch
- What's Single dynamic dispatch?
- Single ones use just one parameter to select a method
- Make an example
class SurveyBase{ public virtual void DoSurvey(){...} } class Survey : SurveyBase{ public override void DoSurvey() {...} } /*****************************Client Code**************************************/ SurveyBase base = new Survey(); base.DoSurvey(); // Survey.DoSurvey will be called
Multiple dynamic dispatch
- What's Multiple dynamic Dispatch?
- The multiple ones can take advantage of as many parameters they want
- Make an example
Output://classes interface IBar {} class Bar : IBar {} sealed class FooBar : Bar {} //methods void Print(IBar item){Console.WriteLine("It is an IBar");} void Print(Bar item){Console.WriteLine("It is a Bar");} void Print(FooBar item){Console.WriteLine("It is a FooBar");} //call methods, var bar = new Bar(); var foo = new FooBar(); IBar ibar = new FooBar(); IBar[] items = { bar, foo, ibar }; foreach (var item in items) { Print(item); } foreach (dynamic item in items) { Print(item); }
It is an IBar. It is an IBar. It is an IBar. It is a Bar. It is a FooBar. It is a FooBar.
Method Hiding and Overriding
- What's Method Hiding?
- Method hiding means Subclass has defined a class method with the same signature as a class method in the superclass. In that case, the method of the superclass is hidden by the subclass.
- What's Method Overriding?
- The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed.