📊Working with Data

Learn to persist, transform, and query data with collections, LINQ, file I/O, and serialization techniques.

📚 Collections and LINQ

Welcome to our comprehensive guide on Collections and LINQ in C#. Mastering these topics will empower you to work effectively with data in your applications. Let’s dive into the core concepts, practical examples, and best practices!

💡 What are Collections?

Collections are data structures that allow you to store and manipulate multiple values. Unlike arrays, which have a fixed size and type, collections provide more flexibility and functionality.

  • Arrays: Simple, fixed-size collections.
  • Lists: Dynamic collections that grow as needed.
  • Dictionaries: Key-value pairs for fast lookups.

Essential Collection Types in C#

  • Arrays: Best for fixed-size data with fast access. Use [T][] syntax.
  • Lists: Ideal for dynamic collections. Use List<T>.
  • Dictionaries: Perfect for key-value storage. Use Dictionary<TKey, TValue>.
// Example of different collections
int[] numbersArray = { 1, 2, 3 };
List<int> numbersList = new List<int> { 4, 5, 6 };
Dictionary<string, int> numberDict = new Dictionary<string, int> { { "one", 1 }, { "two", 2 } };

💡 Introduction to LINQ

LINQ (Language Integrated Query) is a powerful querying tool that simplifies working with collections. It allows you to write readable, concise code for filtering, projecting, and aggregating data.

  • Query Syntax: Mimics SQL with from, select, where clauses.
  • Method Syntax: Uses extension methods like Where(), Select(), Aggregate().
// LINQ Query Syntax
var evenNumbers = from n in numbersList
                   where n % 2 == 0
                   select n;

// LINQ Method Syntax
var evenNumbers = numbersList.Where(n => n % 2 == 0);

Filtering and Projection

Use LINQ to filter data based on conditions and project only the necessary information.

// Filtering
var filtered = numbersList.Where(n => n > 3);

// Projection
var projected = numbersList.Select(n => new { DoubleValue = n * 2 });

💡 Aggregation with LINQ

Perform calculations on collections using aggregation methods like Sum(), Average(), Min(), Max().

// Sum of all numbers
int total = numbersList.Sum();

// Maximum value
int max = numbersList.Max();

Common Mistakes to Avoid

  • Avoid using arrays for dynamic collections.
  • Don’t forget to include System.Linq namespace for LINQ.
  • Be cautious with nested queries that can lead to performance issues.

Real-World Application Example

Let’s see how collections and LINQ work together in a practical scenario: processing customer orders.

// Sample data
List<Order> orders = new List<Order>
{
    new Order { Id = 1, CustomerName = "Alice", Amount = 200 },
    new Order { Id = 2, CustomerName = "Bob", Amount = 300 },
    new Order { Id = 3, CustomerName = "Charlie", Amount = 450 }
};

// LINQ query to calculate total sales for customers with amount > 250
var result = orders.Where(o => o.Amount > 250)
                    .Aggregate(0, (total, order) => total + order.Amount);

Console.WriteLine($"Total Sales: {result}");

💡 Best Practices for Collections and LINQ

  • Choose the right collection based on your needs.
  • Use LINQ for readable and maintainable queries.
  • Optimize performance by avoiding unnecessary projections or filtering.

🗂️ File I/O and Streams

Welcome to File I/O and Streams! In this chapter, you'll learn how to work with files in C# using the `File`, `StreamReader`, and `StreamWriter` classes. You'll discover how to read from and write to files while handling file paths efficiently and ensuring your code is safe from exceptions.

💡 Basic File Operations

Let's start with the fundamentals of reading and writing files in C#.

// Reading a file
string text = File.ReadAllText(@"C:example.txt");
Console.WriteLine(text);

// Writing to a file
File.WriteAllText(@"C:output.txt", "Hello, World!");
  • Use `File.ReadAllText()` to read the entire content of a text file.
  • Use `File.WriteAllText()` to write text to a file, overwriting its contents if it exists.
  • Always use `@` before string literals containing backslashes to handle escape characters properly.

💡 Stream-Based Operations

For more advanced scenarios, especially when dealing with large files or memory constraints, you can use streams.

// Reading from a file using StreamReader
using (StreamReader reader = new StreamReader(@"C:example.txt")) {
    string line;
    while ((line = reader.ReadLine()) != null) {
        Console.WriteLine(line);
    }
}

// Writing to a file using StreamWriter
using (StreamWriter writer = new StreamWriter(@"C:output.txt")) {
    writer.WriteLine("Hello, World!");
}
  • Use `StreamReader` for reading text from files or streams.
  • Use `StreamWriter` for writing text to files or streams.
  • Always wrap stream objects in a `using` statement to ensure they are properly disposed of.

💡 Exception Handling and Best Practices

File operations can throw exceptions if files don't exist or permissions are denied. Here's how to handle them safely.

try {
    string text = File.ReadAllText(@"C:example.txt");
} catch (FileNotFoundException) {
    Console.WriteLine("The file was not found.");
} catch (UnauthorizedAccessException) {
    Console.WriteLine("You don't have permission to access this file.");
} finally {
    // Cleanup code if needed
}
  • Use try-catch blocks to handle potential exceptions.
  • Prefer the `using` statement for disposable objects like streams.
  • Check if a file exists before attempting to write to it using `File.Exists()`.

Best Practices

  • Always use `using` statements for streams and other disposable objects.
  • Handle exceptions at the appropriate level to avoid masking errors.
  • Use fully qualified file paths or ensure your working directory is set correctly.
  • Avoid hardcoding file paths; use environment variables when possible.

Common Mistakes

  • Don't forget to dispose of streams and other disposable objects.
  • Don't assume files exist; always check before attempting operations that require them.
  • Don't use `File.AppendAllText()` without checking if the file exists when you don't want to overwrite.
  • Don't ignore exceptions; handle them gracefully and provide meaningful feedback.

🧾 JSON & XML Serialization

Welcome to our chapter on JSON & XML Serialization! In this section, we'll explore how to serialize and deserialize data using both `System.Text.Json` and `Newtonsoft.Json`. You'll learn robust strategies for handling complex object graphs, null values, and schema changes.

💡 Foundational Concepts

  • Serialization is the process of converting objects to a text-based format for storage or transmission.
  • Deserialization is the reverse process, reconstructing objects from serialized data.
  • JSON (JavaScript Object Notation) is widely used due to its readability and efficiency.
  • XML (Extensible Markup Language) remains important in enterprise environments.

Working with JSON

The `System.Text.Json` namespace provides高性能和类型安全的 JSON 处理功能,适合现代 C# 开发。Newtonsoft.Json 作为社区驱动的替代方案,提供了额外的功能和兼容性。让我们看看如何使用它们:

using System.Text.Json;

var person = new Person { Name = "Alice", Age = 30 };
string json = JsonSerializer.Serialize(person);
Console.WriteLine(json);

💡 Handling Complex Data Structures

When dealing with object graphs that contain nested objects, collections, or circular references, you need to use proper configuration options.

var settings = new JsonSerializerOptions
{
    ReferenceHandling = ReferenceHandling.Preserve,
    WriteIndented = true
};

Null Values and Optional Properties

Gracefully handling nulls is crucial for robust serialization. You can configure how null values are handled using the `NullValueHandling` option.

[ Newtonsoft.Json.JsonProperty(NullValueHandling = NullValueHandling.Ignore) ]

💡 XML Serialization Best Practices

  • Always validate XML schemas to ensure data integrity.
  • Use `XmlSerializer` for simple scenarios and `DataContractSerializer` for more complex cases.
  • Enable proper namespaces using the `XmlNamespaceManager`.
  • Consider performance implications when dealing with large datasets.

Real-World Application Scenarios

Serialization is essential in many aspects of application development, including: - API data exchange - Configuration management - Data persistence - Communication between services

public class Product
{
    [XmlElement("id")]
    public int Id { get; set; }

    [XmlElement("name")]
    public string Name { get; set; }
}

Quiz

Question 1 of 13

Which collection is best suited for fast lookups using keys?

  • List
  • Array
  • Dictionary
  • HashSet