C# Linq(C)中的左外连接是如何工作的?

C# Linq(C)中的左外连接是如何工作的?,c#,linq,join,C#,Linq,Join,左外连接是如何工作的?我知道如何执行左外连接,因此有一些有用的文章,甚至官方MSDN网站也有一个光滑的示例,我将在这里用它来解释我的问题 简而言之,我的问题 from子句可以在同一查询中多次使用,该查询将被视为子查询源:。如果第二个from子句未用于第一个from子句的range元素的子级,则它将执行交叉连接源:。但是,我们在下面的左连接示例中看到,gj.DefaultIfEmpty上的第二个from子句没有给出相同的结果,这是怎么回事?linq查询是否执行某种关联 来自MSDN的示例 来源 M

左外连接是如何工作的?我知道如何执行左外连接,因此有一些有用的文章,甚至官方MSDN网站也有一个光滑的示例,我将在这里用它来解释我的问题

简而言之,我的问题 from子句可以在同一查询中多次使用,该查询将被视为子查询源:。如果第二个from子句未用于第一个from子句的range元素的子级,则它将执行交叉连接源:。但是,我们在下面的左连接示例中看到,gj.DefaultIfEmpty上的第二个from子句没有给出相同的结果,这是怎么回事?linq查询是否执行某种关联

来自MSDN的示例 来源

MSDN中from条款的解释 来源

到目前为止我所拥有的 我猜Linq引擎会这样做

执行联接操作 按第一个加入成员分组 对于不是上一步的空结果的每个第一个联接成员,将groupped元素作为子元素追加
我当然没有任何关于这个想法的参考资料,但这是有意义的,甚至可以解释为什么必须使用DefaultIfEmpty。

请参阅实际的源代码,在那里可以找到这些实现事实。关于LINQ如何实现的详细描述对于堆栈溢出问题来说太宽泛了。谢谢,我很欣赏参考链接请参阅实际的源代码,在那里您可以找到这些实现事实。关于LINQ如何实现的详细描述对于堆栈溢出问题来说太宽泛了。谢谢,我很欣赏参考链接请参阅实际的源代码,在那里您可以找到这些实现事实。关于LINQ是如何实现的详细描述对于堆栈溢出问题来说太宽泛了。谢谢,我很感谢您提供的参考链接
    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    class Pet
    {
        public string Name { get; set; }
        public Person Owner { get; set; }
    }

    public static void LeftOuterJoinExample()
    {
        Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
        Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
        Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
        Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

        Pet barley = new Pet { Name = "Barley", Owner = terry };
        Pet boots = new Pet { Name = "Boots", Owner = terry };
        Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
        Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
        Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

        // Create two lists.
        List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
        List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

        var query = from person in people
                    join pet in pets on person equals pet.Owner into gj
                    from subpet in gj.DefaultIfEmpty()
                    select new { person.FirstName, PetName = (subpet == null ? String.Empty : subpet.Name) };

        foreach (var v in query)
        {
            Console.WriteLine("{0,-15}{1}", v.FirstName + ":", v.PetName);
        }
    }

    // This code produces the following output: 
    // 
    // Magnus:         Daisy 
    // Terry:          Barley 
    // Terry:          Boots 
    // Terry:          Blue Moon 
    // Charlotte:      Whiskers 
    // Arlene:
class CompoundFrom2
{
    static void Main()
    {
        char[] upperCase = { 'A', 'B', 'C' };
        char[] lowerCase = { 'x', 'y', 'z' };

        // The type of joinQuery1 is IEnumerable<'a>, where 'a 
        // indicates an anonymous type. This anonymous type has two 
        // members, upper and lower, both of type char. 
        var joinQuery1 =
            from upper in upperCase
            from lower in lowerCase
            select new { upper, lower };

        // The type of joinQuery2 is IEnumerable<'a>, where 'a 
        // indicates an anonymous type. This anonymous type has two 
        // members, upper and lower, both of type char. 
        var joinQuery2 =
            from lower in lowerCase
            where lower != 'x'
            from upper in upperCase
            select new { lower, upper };


        // Execute the queries.
        Console.WriteLine("Cross join:");
        // Rest the mouse pointer on joinQuery1 to verify its type. 
        foreach (var pair in joinQuery1)
        {
            Console.WriteLine("{0} is matched to {1}", pair.upper, pair.lower);
        }

        Console.WriteLine("Filtered non-equijoin:");
        // Rest the mouse pointer over joinQuery2 to verify its type. 
        foreach (var pair in joinQuery2)
        {
            Console.WriteLine("{0} is matched to {1}", pair.lower, pair.upper);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
        Cross join:
        A is matched to x
        A is matched to y
        A is matched to z
        B is matched to x
        B is matched to y
        B is matched to z
        C is matched to x
        C is matched to y
        C is matched to z
        Filtered non-equijoin:
        y is matched to A
        y is matched to B
        y is matched to C
        z is matched to A
        z is matched to B
        z is matched to C
        */