私有属性设置;

50

我知道它只允许类来设置它,但这有什么意义呢?

如何解决只读id的问题?

假设我有一个人类:

public class Person
    {
        public string Name { get;  set; }
        public int Id { get; private set; }
        public int Age { get; set; }
    }

这段代码在一个名为 Entities.dll 的库中,被GUI、BL和DAL使用。

GUI调用了BL:

   List<Person> p =  BL.PeopleBL.GetPeople();

为了举例,调用数据访问层(DAL):

...
while(dr.read())
{
    returnPersonList.add( new Person{ Age=dr.GetInt32(1), Id=dr.GetInt32(0), Name=dr.GetString(2)})
}
...

当然,我不能这样做,因为Id是一个私有集合;

如何正确处理此问题?

我如何让BL/Dal设置Id,而不是在GUI上设置?

或者这甚至不是使用私有集合的正确方式吗?


我只想补充一下,这是您典型的数据库应用程序,其中pk是Id,不应更改(只能由BL / DAL更改)。



@Eric:从私有设置中你能得到什么?你是在试图保护这个变量不受某些东西的影响吗? - Joel Etherton
16
除非有高度特殊的需求,否则所有内容都应保持私密。不应该有强制性的原因来保护会员,这是默认情况。 - Rex M
9个回答

42

这是一个可能的解决方案,尽管不是非常干净:

  1. 将需要暴露给 BAL 和 DAL 的属性设置为 internal
  2. assemblyinfo.cs 中标记 BAL.dllDAL.dllInternal Visible

public class Person
{
    public Person(int id)
    {
         this.Id=id;
    }

    public string Name { get;  set; }
    public int Id { get; internal set; }
    public int Age { get; set; }
}

AssemblyInfo.cs 是针对 Entities.dll 的。

[assembly: InternalsVisibleTo("DAL"), InternalsVisibleTo("BAL")]

这样所有的内部实现都将对数据访问层和业务逻辑层可见。这可能不是一个理想的结果,但我只是提供了一种可能的解决方案。


我想知道为什么这没有得到点赞。这是问题的错误解决方案吗?听起来好像我用这个可以得到最好的结果。 - Eric
如果我将BL/DAL/Entities合并到ILMerged中,这是否仍然需要? - Eric

18

两种常见的方法是:要么类应该具有供数据访问层使用的构造函数,要么数据访问层应该使用反射来填充对象。


我以前用过CTor,但感觉不对。这让我想为什么要使用set,而不是使用带有后备字段的get。你可以猜到Id只是数据存储中的主键,不应该被更改。 - Eric
我将接受这个作为最符合解决方案,但我会尝试下面的InternalsVisibleTo答案。 - Eric
@Rex M,我认为您是正确的,我从未查看过。但对于问题提出的最简单解决方案,它是一个ctor。感谢您提供的信息。 - kenny

15

或者你可以这样做

public class Person
{
    public Person(int id)
    {
         this.Id=id;
    }

    public string Name { get;  set; }
    public int Id { get; private set; }
    public int Age { get; set; }
}

我以前用过CTor,但感觉不对。这让我想为什么要使用set,而不是使用带有后备字段的get。你可以猜到Id只是数据存储中的主键,不应该被更改。 - Eric

9
也许我理解有误,但是如果您想要真正的只读ID,为什么不使用实际的只读字段呢?
public class Person
{
   public Person(int id)
   {
      m_id = id;
   }

   readonly int m_id;
   public int Id { get { return m_id; } }
}

7
你可以通过构造函数提供只读属性来让用户设置它:
public class Person
{
    public Person(int id)
    {
        this.Id = id;
    }

    public string Name { get;  set; }
    public int Id { get; private set; }
    public int Age { get; set; }
}

我以前用过CTor,但感觉不对。这让我想为什么要使用set,而不是使用带有后备字段的get。正如你所猜测的那样,Id只是数据存储中的主键,不应更改。 - Eric

2
while(dr.read())
{
    returnPersonList.add( 
        new Person(dr.GetInt32(1), dr.GetInt32(0), dr.GetString(2)));
}

其中:

public class Person
{
    public Person(int age, int id, string name) 
    {
        Age = age;
        Id = id;
        Name = name;
    }
}

嗨 John - 我只是在 Stack Overflow 上浏览一下,等待构建,看看能否学到一些东西。我很好奇。你是不是想留下 Id= 和 Name=? - J M
@JM:是的,这是一个构造函数。我为什么要删除它们呢? - John Saunders
我知道这是一个构造函数 :) 我很好奇因为我无法编译它。我复制了带有您的构造函数和3个属性的类。我单独调用了构造函数,即 var x = new Person(20, Id=1, Name="X"); 这会出现错误:“当前上下文中不存在名称'Id'”,Name也是同样的情况。如果不使用 Id= 和 Name= 调用构造函数,则可以正常工作,并且如果添加默认构造函数,则 var y = new Person() {Name = "Blah", Age = 20}; 可以正常工作。我不知道您是否可以混合两者,所以我想尝试一下。如果可能的话,您有什么想法为什么我会收到错误信息? - J M
@约翰:new Person(dr.GetInt32(1), Id=dr.GetInt32(0), Name=dr.GetString(2))) - Tanzelax
@JM:我以为你指的是构造函数体中的那些。很好地发现了我的复制/粘贴错误(现在已经修复)。 - John Saunders
显示剩余2条评论

2
也许,您可以将它们标记为内部的,在这种情况下,只有DAL或BL中的类(假设它们是单独的dll)才能设置它。
您还可以提供一个构造函数,该构造函数接受字段,然后仅公开它们作为属性。

这是我尝试过的,非常好用.. [assembly: InternalsVisibleTo("testDAL")] - Eric

1

在实体中,如果ID不是其自然部分而是数据库需要抽象的构件,则通常会出现这种情况。

这是一种设计决策 - 仅允许在构建期间或通过方法调用设置ID,以便该类内部进行管理。

假设您有一个支持字段,您可以自行编写setter:

private int Id = 0;
public void SetId (int id)
{
  this.Id = id;
}

或者通过构造函数:

private int Id = 0;
public Person (int id)
{
  this.Id = id;
}

我认为如果我使用了setId,那么我的API的任何消费者都可以更改它,这正是我不想要的。但我以前就是这样做的。 - Eric

0
根据我的应用程序范围,我喜欢将对象水合机制放在对象本身中。我会使用自定义对象包装数据读取器,并传递一个委托,在查询返回时执行该委托。该委托接收DataReader。然后,由于我在智能业务对象中,因此我可以使用私有setter进行水化。
为伪代码编辑
“DataAccessWrapper”为我包装了所有连接和对象生命周期管理。所以,当我调用“ExecuteDataReader”时,它创建连接,使用传递的proc(params有一个重载)执行它,执行委托,然后清理自己。
public class User
{
    public static List<User> GetAllUsers()
    {
        DataAccessWrapper daw = new DataAccessWrapper();
        return (List<User>)(daw.ExecuteDataReader("MyProc", new ReaderDelegate(ReadList)));
    }

    protected static object ReadList(SQLDataReader dr)
    {
        List<User> retVal = new List<User>();
        while(dr.Read())
        {
            User temp = new User();
            temp.Prop1 = dr.GetString("Prop1");
            temp.Prop2 = dr.GetInt("Prop2");
            retVal.Add(temp);
        }
        return retVal;
    }
}

这听起来很有趣,你能分享一些伪代码吗? - Eric
感谢您添加这个,看起来对我不起作用,因为在我的实现中,我的实体(用户)存在于许多层中,对数据存储一无所知。 - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接