Взгляните на этот пример . Этот запрос должен работать:
var leftFinal = from left in lefts
join right in rights on left equals right.Left into leftRights
from leftRight in leftRights.DefaultIfEmpty()
select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 };
Классы вариантов можно рассматривать как простые и неизменяемые объекты хранения данных, которые должны зависеть исключительно от своих аргументов конструктора .
Эта функциональная концепция позволяет нам
Узел (1, Leaf (2), None))
) В сочетании с наследованием классы case используются для имитации алгебраических типов данных .
Если объект выполняет вычисления с отслеживанием состояния внутри или демонстрирует другие виды сложного поведения, это должен быть обычный класс.
Некоторые основные характеристики case classes
упоминаются ниже
new
ключевое слово. Образец scala код скрипки scala, взятой из scala документов.
(Вы уже упомянули все, кроме последнего).
Это единственные отличия от обычных классов.
Технически нет никакой разницы между классом и классом case - даже если компилятор действительно оптимизирует некоторые вещи при использовании классов case. Однако класс case используется для того, чтобы избавиться от шаблонов для определенного шаблона, который реализует алгебраические типы данных .
Очень простой пример таких типов - деревья. Бинарное дерево, например, может быть реализовано следующим образом:
sealed abstract class Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf[A](value: A) extends Tree
case object EmptyLeaf extends Tree
Это позволяет нам делать следующее:
// DSL-like assignment:
val treeA = Node(EmptyLeaf, Leaf(5))
val treeB = Node(Node(Leaf(2), Leaf(3)), Leaf(5))
// On Scala 2.8, modification through cloning:
val treeC = treeA.copy(left = treeB.left)
// Pretty printing:
println("Tree A: "+treeA)
println("Tree B: "+treeB)
println("Tree C: "+treeC)
// Comparison:
println("Tree A == Tree B: %s" format (treeA == treeB).toString)
println("Tree B == Tree C: %s" format (treeB == treeC).toString)
// Pattern matching:
treeA match {
case Node(EmptyLeaf, right) => println("Can be reduced to "+right)
case Node(left, EmptyLeaf) => println("Can be reduced to "+left)
case _ => println(treeA+" cannot be reduced")
}
// Pattern matches can be safely done, because the compiler warns about
// non-exaustive matches:
def checkTree(t: Tree) = t match {
case Node(EmptyLeaf, Node(left, right)) =>
// case Node(EmptyLeaf, Leaf(el)) =>
case Node(Node(left, right), EmptyLeaf) =>
case Node(Leaf(el), EmptyLeaf) =>
case Node(Node(l1, r1), Node(l2, r2)) =>
case Node(Leaf(e1), Leaf(e2)) =>
case Node(Node(left, right), Leaf(el)) =>
case Node(Leaf(el), Node(left, right)) =>
// case Node(EmptyLeaf, EmptyLeaf) =>
case Leaf(el) =>
case EmptyLeaf =>
}
Обратите внимание, что деревья конструируются и деконструируются (посредством сопоставления с образцом) с одним и тем же синтаксисом, который также является точным, как они есть напечатано (без пробелов).
И их также можно использовать с хэш-картами или наборами, поскольку они имеют действительный и стабильный хэш-код.