【问题标题】:LINQ GroupBy not grouping resultsLINQ GroupBy 不分组结果
【发布时间】:2019-09-03 19:18:36
【问题描述】:

我正在尝试按 Email 属性对常见对象的列表进行分组,但没有得到我期望的结果。

var users = new List<Profile>
            {
                new SharePointProfile
                {
                    Id = 1,
                    LoginName = "Login1",
                    Title = "Login1 SharePoint",
                    Email = "Login1@sharepoint.com",
                    IsSiteAdmin = false
                },
                new SharePointProfile
                {
                    Id = 2,
                    LoginName = "Login2",
                    Title = "Login2 SharePoint",
                    Email = "Login2@sharepoint.com",
                    IsSiteAdmin = true
                },
                new Auth0Profile
                {
                    Email = "Login2@sharepoint.com",
                    Name = "Login2 SharePoint Auth0"
                },
                new Auth0Profile
                {
                    Email = "test@test.com",
                    Name = "Test User Auth0"
                }
            };

var userGroups = users.GroupBy(m => m.Email)

我希望看到三个组:

  • 组 #1 - Login1@sharepoint.com - 1 个 SharePointProfile 对象
  • 第 2 组 - Login2@sharepoint.com - 1 个 SharePointProfile 对象和 1 个 Auth0Profile 对象
  • 第 3 组 - test@test.com - 1 个 Auth0Profile 对象

我在这里做错了什么?

public abstract class Profile : IEquatable<Profile>
    {
        public string Name { get; set; }
        public string Email { get; set; }

        public bool Equals(Profile other)
        {
            return this.Email == other.Email;
        }
    }

    public class Auth0Profile : Profile
    {
        public new string Email { get; set; }
        public new string Name { get; set; }
        public string Connection { get; set; }
        public string Password { get; set; }
        public bool VerifyEmail { get; set; }
    }

    public class SharePointProfile : Profile
    {
        public int Id { get; set; }
        public string LoginName { get; set; }
        public string Title { get; set; }
        public new string Email { get; set; }
        public bool IsSiteAdmin { get; set; }
    }

【问题讨论】:

  • 您能否发布您的 3 个类(Profile、SharePointProfile 和 Auth0Profile)的(缩短)版本?是否有任何覆盖/操作子类中的电子邮件属性?
  • @gunr2171 感谢您的评论。我已经添加了这些,以便您查看。
  • 幽默,请注释掉 Auth0Profile 和 SharePointProfile(但不是 Profile)中的电子邮件属性,然后再试一次。
  • 是的,事实上您在属性中使用了new。我对此没有很好的解释,所以有人可以在这里跳出来并做出更好的回答。底线 - 如果没有充分的理由,请勿在属性(或继承)中使用 new。
  • 当您添加new 时,它会创建一个与基类的Email 属性完全无关的新属性(也称为Email)。通过摆脱new 版本,您的所有类现在都具有相同的Email 属性,因此它们的实例可以正确地组合在一起。如果您离开new,您的子类将有两个不相关的Email 属性,一个可通过base.Email 访问,另一个可通过this.Email 访问。你可以不同地设置它们

标签: c# linq


【解决方案1】:

问题是您的列表中有 Profile 类型的对象。但是在您对对象的定义中,您会在每个对象上创建名为 email 的新属性。 linq 会查看未使用的属性。

【讨论】:

    【解决方案2】:

    在您的代码中,您有几个继承问题。 将您的课程更新为:

       public abstract class Profile : IEquatable<Profile>
      {
        public string Name { get; set; }
        public string Email { get; set; }
    
        public bool Equals(Profile other)
       {
           return this.Email == other.Email;
        }
     }
    
    public class Auth0Profile : Profile
    {
    
       public string Connection { get; set; }
       public string Password { get; set; }
       public bool VerifyEmail { get; set; }
    }
    
    public class SharePointProfile : Profile
    {
       public int Id { get; set; }
       public string Title { get; set; }
       public bool IsSiteAdmin { get; set; }
    }
    

    分组会起作用

    【讨论】:

      【解决方案3】:

      其他答案更多地倾向于解决问题的方法,但对于为什么它首先是一个问题,他们在细节上有点肤浅

      考虑这组简单的类:

      class Foo:IEquatable<Foo>{
        public string A="foo";
        public bool Equals(Foo other){
          return this.A == other.A;
        }
      }
      class Bar:Foo{
        public new string A="bar";
      }
      class Baz:Foo{
        public new string A="baz";
      }
      

      Bar 和 Baz 都可以装箱为 Foo:

      Foo br = new Bar();
      Foo bz = new Baz();
      

      当您将它们放入类型为 Profiles/Foos 的列表中时也会发生这种情况

      无论如何,即使我们的 Bar 和 Baz 有自己的 A 值,当它们被装箱为 Foo 时,它们都将使用 Foo 的 A。它们有自己的 A 值,因为 A 在子类中被声明为新的。然后,您可以根据它们被装箱的类型选择它们具有的两个 A 中的哪一个:

      Foo b = new Bar(); //print(b.A) would show "foo"
      Bar b = new Bar(); //print(b.A) would show "bar"
      

      如果他们被投射/装箱为 Bar/Baz,那么他们会使用 Bar 中的 A。如果他们被投射/装箱为 Foo 那么他们在 Foo 中使用 A

      这也会影响您的 Equals 比较:

      Bar br = new Bar(); 
      Baz bz = new Baz();
      br.Equals(bz); //true, the comparison is done inside Foo, using objects that are boxed as Foo
                     //this means that it is "foo" == "foo" that is compared (true) 
                     //rather than "bar" == "baz" (false) 
      

      您的分组最终得到了一组 4 个实体,因为它们都使用 Foo 中的 A 并最终归入“foo”(或者,在您的情况下为 null Profile.Email 版本,因此最终归入 null)

      通过从 Bar 和 Baz 中删除 new 关键字,编译器会抱怨 A 已在父级和子级中声明。这是一个警告,而不是错误,因为子项中的 A 隐藏了父项中的 A 就像您使用 new 关键字一样 - new 在这种情况下真正做的唯一事情是有效地声明“我知道我是什么”我在做,不要再警告我 Bar.A 隐藏了 Foo.A"。

      您应该从 Bar 和 Baz 中删除 A 的定义,而不是添加 new 来停止警告,这样他们就可以使用在父 Foo 中声明的 A 的单一存在。这样Bar 和Baz 都将拥有A 属性,这要归功于它们的父级拥有该属性,而不是拥有自己的A。因为Bar.A 和Baz.A 实际上只是Foo 中的A,这意味着Bar/Baz 不可能有一个不同于A 的值Foo.A - 所以当Bar.A 设置为"bar" 并且Baz.A 设置为"baz" 时,在考虑Foos 的列表时,分组将看到两个不同的值,你最终会得到分组下的东西"bar" 和 "baz"

      最终new 有点违反继承的工作方式;公共属性应该存在于父类上,抽象Vehicle应该有NumberOfWheels,而Car子类应该将其设置为4,而不是重新声明它自己的NumberOfWheels。 new 的有效用例非常狭窄;根据经验,你可能永远不需要使用它——如果你认为你这样做了,那么其他地方更有可能出现问题。请参阅Overriding vs method hiding,了解更多关于为什么使用它的讨论。在我的整个职业生涯中,我从来不需要这样做,而且我已经很老了

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-23
        相关资源
        最近更新 更多