Giter VIP home page Giter VIP logo

zt1103.github.io's Introduction

This is a LINQ tutorial with a light focus on usefulness in small indie game dev.

What is LINQ?

Language-Integrated Query (LINQ) is the name for a set of technologies based on the integration of query capabilities directly into the C# language. Traditionally, queries against data are expressed as simple strings without type checking at compile time or IntelliSense support. Furthermore, you have to learn a different query language for each type of data source: SQL databases, XML documents, various Web services, and so on. With LINQ, a query is a first-class language construct, just like classes, methods, and events.

https://learn.microsoft.com/en-us/dotnet/csharp/linq/

Why do I need to learn LINQ?

You don't. But, a significant portion of the time, you can compress multiple lines of code down to just one.

A "quick" example:

Let's say we have a custom class to hold Cards. Cards have a value and a suit.

public class Card{
	Suit suit;
	Value value;
	public override string ToString()
	{
		return $"{value} of {suit}";
	}
	public Card(Suit s, Value v){
		this.suit = s;
		this.value = v;
	}
}
public enum Suit{
	Diamond,
	Clubs,
	Hearts,
	Spades
}
public enum Value{
	Ace,
	Two,
	Three,
	Four,
	Five,
	Six,
	Seven,
	Eight,
	Nine,
	Ten,
	Jack,
	Queen,
	King
}

You want to create the deck. You could write out 52 lines of code, one to add each card (Ace of Spades, Two of Spades, .... King of Hearts):

this.Add(new Card( Suit.Diamond,Value.Ace));
		this.Add(new Card(Suit.Diamond, Value.Two));
		this.Add(new Card(Suit.Diamond, Value.Three));
		this.Add(new Card(Suit.Diamond, Value.Four));
		this.Add(new Card(Suit.Diamond, Value.Five));
		this.Add(new Card(Suit.Diamond, Value.Six));
		this.Add(new Card(Suit.Diamond, Value.Seven));
		this.Add(new Card(Suit.Diamond, Value.Eight));
		this.Add(new Card(Suit.Diamond, Value.Nine));
		this.Add(new Card(Suit.Diamond, Value.Ten));
		this.Add(new Card(Suit.Diamond, Value.Jack));
		this.Add(new Card(Suit.Diamond, Value.Queen));
		this.Add(new Card(Suit.Diamond, Value.King));
		this.Add(new Card(Suit.Clubs, Value.Ace));
		this.Add(new Card(Suit.Clubs, Value.Two));
		this.Add(new Card(Suit.Clubs, Value.Three));
		this.Add(new Card(Suit.Clubs, Value.Four));
		this.Add(new Card(Suit.Clubs, Value.Five));
		this.Add(new Card(Suit.Clubs, Value.Six));
		this.Add(new Card(Suit.Clubs, Value.Seven));
		this.Add(new Card(Suit.Clubs, Value.Eight));
		this.Add(new Card(Suit.Clubs, Value.Nine));
		this.Add(new Card(Suit.Clubs, Value.Ten));
		this.Add(new Card(Suit.Clubs, Value.Jack));
		this.Add(new Card(Suit.Clubs, Value.Queen));
		this.Add(new Card(Suit.Clubs, Value.King));
		this.Add(new Card(Suit.Hearts, Value.Ace));
		this.Add(new Card(Suit.Hearts, Value.Two));
		this.Add(new Card(Suit.Hearts, Value.Three));
		this.Add(new Card(Suit.Hearts, Value.Four));
		this.Add(new Card(Suit.Hearts, Value.Five));
		this.Add(new Card(Suit.Hearts, Value.Six));
		this.Add(new Card(Suit.Hearts, Value.Seven));
		this.Add(new Card(Suit.Hearts, Value.Eight));
		this.Add(new Card(Suit.Hearts, Value.Nine));
		this.Add(new Card(Suit.Hearts, Value.Ten));
		this.Add(new Card(Suit.Hearts, Value.Jack));
		this.Add(new Card(Suit.Hearts, Value.Queen));
		this.Add(new Card(Suit.Hearts, Value.King));
		this.Add(new Card(Suit.Spades, Value.Ace));
		this.Add(new Card(Suit.Spades, Value.Two));
		this.Add(new Card(Suit.Spades, Value.Three));
		this.Add(new Card(Suit.Spades, Value.Four));
		this.Add(new Card(Suit.Spades, Value.Five));
		this.Add(new Card(Suit.Spades, Value.Six));
		this.Add(new Card(Suit.Spades, Value.Seven));
		this.Add(new Card(Suit.Spades, Value.Eight));
		this.Add(new Card(Suit.Spades, Value.Nine));
		this.Add(new Card(Suit.Spades, Value.Ten));
		this.Add(new Card(Suit.Spades, Value.Jack));
		this.Add(new Card(Suit.Spades, Value.Queen));
		this.Add(new Card(Suit.Spades, Value.King));

You could write nested loops and loop through values and suits, create cards and add them to the list:

public class Deck:List<Card>
{
	public Deck()
	{
		foreach (Suit s in Enum.GetValues<Suit>())
		{
			foreach (Value v in Enum.GetValues<Value>())
			{
				this.Add(new Card(s, v));
			}
		}
	}
}

Or using LINQ, you could:

public class Deck:List<Card>
{
	public Deck()
	{
		this.AddRange(Enum.GetValues<Suit>().SelectMany(x => Enum.GetValues<Value>(), (s,v) => new Card(s,v)));
	}
}

Now you might say "Whoa, that's a long line of code that I don't understand at all. At least I can look at the loops and understand what's happening"

There's absolutely nothing wrong with that. 7 lines of code you understand is better than 1 line of code you don't.

But you CAN understand the long line of code. Let's break this line of code down a little bit

this.AddRange(Enum.GetValues<Suit>().SelectMany(suit1 => Enum.GetValues<Value>(), (suit, value) => new Card(suit, value)));
this

is a keyword. We're in the constructor, so we're referring to the "Deck" that we are currently creating an instance of. Deck extends List, so really in this case we're just referring to a list

.AddRange(

AddRange is a method in System.Collections. It accepts one parameter, anything that implements IEnumerable. is a generic type and will be explained in more depth elsewhere Effectively, anything inside the parentheses will be added to the referenced collection.

Enum.GetValues<Suit>().SelectMany(suit1 => Enum.GetValues<Value>(), (suit, value) => new Card(suit, value))

Here's where things get fun! (or scary). All of this together is a IEnumerable with 52 elements, one of each suit/value combination. But how does it work?

There's a few things to note in here that will make explaining this part easier.

The first is there are two instances of

Enum.GetValues<T>() 

one where T is Suit, and one where T is Value. This converts our Value and Suit enums into proper Enumerables that we can use LINQ functions on.

The second thing to note is there are two lamba functions embedded into it. We can break these out if we want, but let's try not to for now.

.SelectMany is an extension method in System.Linq. It's signature (in this case) looks like this

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)

We'll talk about how to read this properly in our Extension article. For now, what you need to know is if you have a IEnumerable (a generic), and you use SelectMany on it, you can give it a Function that gives you a collection of intermediade objects (collectionSelector), and a Function that takes an item of Type TSource, an item of Type TCollection, and returns an item of TResult (resultSelector), and then it will run the function on each element of both collections and combine everything for you.

Let's apply that in real language to our example:

Enum.GetValues<Suit>()

is our IEnumerable of that we are running SelectMany on

We have to tell it how to get from one object of type Suit (suit1) to a collection of Values, and then from one Suit and one Value, how to get to one Card.

suit1 => Enum.GetValues<Value>(), (suit, value) => new Card(suit, value)

Here's our Collection Selector Function

suit1 => Enum.GetValues<Value>()

Given a suit, The list of Values we need for it are all the values in our enum

Then, following our comma, we have to tell it how to get from a Suit and a Value, to a Card: The types here are inferred by the compiler, but you could also be explicit if you wanted (like on the second line)

 	(suit, value) => new Card(suit, value)
	(Suit suit, Value value) => new Card(suit, value)
Enum.GetValues<Suit>().SelectMany(suit1 => Enum.GetValues<Value>(), (suit, value) => new Card(suit, value))

Now put it all back together:

For Each Suit in the enum Suit, get a list of the Values in the Value enum, and then, for each Suit and Value pair, create a new card

this.AddRange(Enum.GetValues<Suit>().SelectMany(suit1 => Enum.GetValues<Value>(), (suit, value) => new Card(suit, value)));

take the resulting IEnumerable and add it to the instance of this Deck object.

And that is one use of SelectMany!

Topics to be covered in the future

Lambda Functions and you

Generic Types

Interfaces

Other LINQ Functions

Extension Methods more generally

zt1103.github.io's People

Contributors

zt1103 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.