404 Not Found

404 Not Found


nginx

Advanced Features Overview

Reflection

Reflection allows a program to inspect and manipulate its own type information at runtime. The Type class is the core of reflection, providing access to metadata such as fields, properties, and methods.

Getting Type Information

CSHARP
using System;
using System.Reflection;

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public void SayHello() => Console.WriteLine("Hello!");
}

class Program
{
    static void Main()
    {
        Type type1 = typeof(Person);
        Type type2 = new Person().GetType();

        Console.WriteLine($"Type name: {type1.Name}");

        Console.WriteLine("Properties:");
        foreach (PropertyInfo p in type1.GetProperties())
            Console.WriteLine($"  {p.Name} ({p.PropertyType.Name})");

        Console.WriteLine("Methods:");
        foreach (MethodInfo m in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
            Console.WriteLine($"  {m.Name}");
    }
}
TEXT
Type name: Person
Properties:
  Name (String)
  Age (Int32)
Methods:
  get_Name
  set_Name
  get_Age
  set_Age
  SayHello

Creating Instances Dynamically

CSHARP
using System;
using System.Reflection;

class Dog
{
    public string Breed { get; set; } = "Unknown";
    public override string ToString() => $"Dog: {Breed}";
}

class Program
{
    static void Main()
    {
        Type type = typeof(Dog);
        object obj = Activator.CreateInstance(type)!;

        PropertyInfo prop = type.GetProperty("Breed")!;
        prop.SetValue(obj, "Husky");

        Console.WriteLine(obj);
    }
}
TEXT
Dog: Husky

Example

CSHARP
using System;
using System.Linq;
using System.Reflection;

class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Multiply(double x, double y) => x * y;
    public void Reset() { }
}

class Program
{
    static void Main()
    {
        Calculator calc = new Calculator();
        Type type = calc.GetType();

        Console.WriteLine($"Type: {type.Name}");
        Console.WriteLine($"Namespace: {type.Namespace}");
        Console.WriteLine($"Is class: {type.IsClass}");

        Console.WriteLine("Public methods:");
        MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        foreach (MethodInfo m in methods)
        {
            string parameters = string.Join(", ", m.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
            Console.WriteLine($"  {m.ReturnType.Name} {m.Name}({parameters})");
        }
    }
}
▶ Try it Yourself
TEXT
Type: Calculator
Namespace: 
Is class: True
Public methods:
  Int32 Add(Int32 a, Int32 b)
  Double Multiply(Double x, Double y)
  Void Reset()

Attributes

Attributes are declarative tags used to add metadata to code elements. You can use built-in attributes or define custom ones.

Built-in Attributes

CSHARP
using System;

class LegacyService
{
    [Obsolete("Please use NewService instead", error: false)]
    public void OldMethod() => Console.WriteLine("Old method");

    public void NewMethod() => Console.WriteLine("New method");
}

class Program
{
    static void Main()
    {
        var service = new LegacyService();
        service.OldMethod();
        service.NewMethod();
    }
}
TEXT
Old method
New method
💡 When the error parameter of Obsolete is set to true, calling the old method will produce a compile error instead of a warning.

Custom Attributes

CSHARP
using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
class DescriptionAttribute : Attribute
{
    public string Text { get; }
    public DescriptionAttribute(string text) => Text = text;
}

[Description("User management service")]
class UserService
{
    [Description("Get all users")]
    public void GetAll() { }
}

class Program
{
    static void Main()
    {
        var attr = Attribute.GetCustomAttribute(typeof(UserService), typeof(DescriptionAttribute)) as DescriptionAttribute;
        Console.WriteLine(attr?.Text ?? "No description");
    }
}
TEXT
User management service

Generic Constraints In Depth

Generic constraints use the where keyword to restrict the range of type parameters, ensuring they meet specific conditions.

Constraint Types Overview

Constraint Description
where T : class T must be a reference type
where T : struct T must be a value type (cannot be null)
where T : new() T must have a parameterless public constructor
where T : ISpecific T must implement the specified interface
where T : BaseClass T must inherit from the specified base class
where T : unmanaged T must be an unmanaged type
where T : notnull T must be a non-nullable type

Example

CSHARP
using System;

interface IRepository
{
    string GetName();
}

class SqlRepository : IRepository
{
    public string GetName() => "SQL Repository";
}

class Service<T> where T : class, IRepository, new()
{
    private T _repo = new T();
    public void Print() => Console.WriteLine(_repo.GetName());
}

class Program
{
    static void Main()
    {
        var service = new Service<SqlRepository>();
        service.Print();
    }
}
▶ Try it Yourself
TEXT
SQL Repository
⚠️ The new() constraint must appear last in the constraint list; class or struct should come first.

Iterators (yield)

Iterators use yield return to produce sequence elements one at a time, implementing lazy evaluation — elements are computed only when iterated.

yield return and yield break

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable<int> GetNumbers()
    {
        for (int i = 0; i < 5; i++)
        {
            if (i == 3) yield break;
            yield return i;
        }
    }

    static void Main()
    {
        foreach (int n in GetNumbers())
            Console.WriteLine(n);
    }
}
TEXT
0
1
2
💡 yield break immediately terminates iteration. Iterator methods return IEnumerable<T> or IEnumerator<T>.

Example

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable<int> Fibonacci(int count)
    {
        int a = 0;
        int b = 1;
        for (int i = 0; i < count; i++)
        {
            yield return a;
            int temp = a + b;
            a = b;
            b = temp;
        }
    }

    static void Main()
    {
        Console.WriteLine("Fibonacci sequence, first 10 terms:");
        foreach (int n in Fibonacci(10))
        {
            Console.Write(n + " ");
        }
        Console.WriteLine();
    }
}
▶ Try it Yourself
TEXT
Fibonacci sequence, first 10 terms:
0 1 1 2 3 5 8 13 21 34 

Span<T> and Memory<T>

Span<T> and Memory<T> provide copy-free sliced access to contiguous memory regions, making them key types for high-performance scenarios.

Span<T> Basic Usage

CSHARP
using System;

class Program
{
    static void Main()
    {
        int[] array = { 10, 20, 30, 40, 50 };
        Span<int> span = array.AsSpan();
        Span<int> slice = span.Slice(1, 3);

        slice[0] = 99;

        Console.WriteLine(string.Join(", ", array));

        Span<int> stackSpan = stackalloc int[3];
        stackSpan[0] = 1;
        stackSpan[1] = 2;
        stackSpan[2] = 3;
        Console.WriteLine(string.Join(", ", stackSpan.ToArray()));
    }
}
TEXT
10, 99, 30, 40, 50
1, 2, 3
⚠️ Span<T> can only be stored on the stack; it cannot be used as a class field or across an await boundary. Use Memory<T> when you need to span across stack frames.

Regular Expressions (Regex)

The System.Text.RegularExpressions.Regex class is used for pattern matching, searching, and replacing text.

Common Operations

CSHARP
using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        string input = "Contact phone: 138-1234-5678, Email: test@example.com";

        bool hasPhone = Regex.IsMatch(input, @"\d{3}-\d{4}-\d{4}");
        Console.WriteLine($"Contains phone: {hasPhone}");

        Match phoneMatch = Regex.Match(input, @"\d{3}-\d{4}-\d{4}");
        Console.WriteLine($"Phone: {phoneMatch.Value}");

        MatchCollection emails = Regex.Matches(input, @"[\w.]+@[\w.]+");
        foreach (Match m in emails)
            Console.WriteLine($"Email: {m.Value}");

        string replaced = Regex.Replace(input, @"\d{4}", "****");
        Console.WriteLine($"Replaced: {replaced}");
    }
}
TEXT
Contains phone: True
Phone: 138-1234-5678
Email: test@example.com
Replaced: Contact phone: 138-**-**, Email: test@example.com

Common Patterns Quick Reference

Pattern Description
\d Digit
\w Letter/digit/underscore
\s Whitespace
. Any character (except newline)
^ / $ Start of line / End of line
{n,m} Repeat n to m times
[abc] Character set

Unsafe Code

The unsafe context allows the use of pointers and manual memory operations, applicable for interop or extreme performance scenarios.

Pointers and fixed

CSHARP
using System;

class Program
{
    static unsafe void Main()
    {
        int value = 42;
        int* ptr = &value;

        Console.WriteLine($"Value: {*ptr}");
        Console.WriteLine($"Address: {(long)ptr}");

        int[] arr = { 1, 2, 3 };
        fixed (int* p = arr)
        {
            Console.WriteLine($"First element: {*p}");
            Console.WriteLine($"Second element: {*(p + 1)}");
        }
    }
}
TEXT
Value: 42
Address: 12345678
First element: 1
Second element: 2
⚠️ Using unsafe requires enabling <AllowUnsafeBlocks>true</AllowUnsafeBlocks> in the project file. The fixed statement pins an object to prevent the GC from moving the memory.

C# Version Features Overview (7–12)

Version Key Features Brief Description
C# 7 out variables, tuples, pattern matching out int x inline declaration, (int, string) tuples, is Type pattern
C# 7 local functions, ref returns Nested functions within methods, reference return values
C# 8 nullable reference types, async streams string? nullable annotation, IAsyncEnumerable<T>
C# 8 default interface methods, indices and ranges Default interface implementations, [^1], [1..3]
C# 9 record types, init setters record immutable types, init init-only properties
C# 9 top-level statements, target-typed new No Main boilerplate, new() type omission
C# 10 global using, file-scoped namespaces global using X;, namespace Ns; single line
C# 10 record structs, const interpolated strings record struct, constant interpolated strings
C# 11 raw string literals, required """...""" multi-line raw strings, required forced initialization
C# 11 list patterns, UTF8 literals [1, 2, ..] matching, "hello"u8
C# 12 primary constructors class C(int x) class declaration as constructor
C# 12 collection expressions, alias any type [1, 2, 3] uniform initialization, using P = int*
💡 This lesson is an overview of advanced features; each topic will be covered in depth in a dedicated follow-up lesson.

❓ FAQ

Q Does reflection have a performance cost?
A Yes, reflection is tens of times slower than direct calls. For frequent use, cache MethodInfo or switch to expression trees. Q: What is the difference between Span&lt;T> and Memory&lt;T>? A: Span can only live on the stack and cannot cross async/storage boundaries; Memory can live on the heap and cross async boundaries, accessed via .Span. Q: Is yield return multithreaded? A: No, yield is a single-threaded lazy enumeration mechanism — elements are computed only when MoveNext() is called. Q: When do you need unsafe code? A: Use it when you need C/C++ interop, unmanaged memory operations, or extreme performance; avoid it in everyday development.

📖 Summary

  • Reflection accesses type metadata at runtime through the Type class and can dynamically create instances
  • Attributes add declarative metadata to code, supporting custom definitions and runtime reading
  • Generic constraints (where) restrict type parameters to reference types, value types, types with constructors, etc.
  • yield return implements lazy iteration; yield break terminates enumeration
  • Span<T> provides stack-based zero-copy slicing; Memory<T> can cross async boundaries
  • The Regex class supports pattern matching, searching, replacing, and other text operations
  • The unsafe context allows pointer operations, requiring fixed to pin memory
  • C# 7–12 continuously introduced tuples, nullable references, record types, primary constructors, and more

📝 Exercises

  1. Use reflection to get all public method names of the string class and output them
  2. Create a custom [Author] attribute, apply it to a class, and read it via reflection
  3. Write a generic method T Create<T>() where T : new() that creates and returns an instance
  4. Use yield return to implement an iterator that generates the first 20 terms of the Fibonacci sequence
  5. Use Regex to validate whether a string is a valid IPv4 address format
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏