Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
c#只读对象_C#_Object_Readonly_Reference Type - Fatal编程技术网

c#只读对象

c#只读对象,c#,object,readonly,reference-type,C#,Object,Readonly,Reference Type,有没有办法返回对象的只读实例 public class Person { public String FirstName { get; set; } public String LastName { get; set; } } public class SomeClass { public SomeClass(Person manager) { if (manager == null) throw new Argument

有没有办法返回对象的只读实例

public class Person
{
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

public class SomeClass
{
    public SomeClass(Person manager)
    {
        if (manager == null)
            throw new ArgumentNullException("manager");

        _manager = manager;
    }

    private readonly Person _manager;
    public Person Manager
    {
        get { return _manager; } //How do I make it readonly period!
    }
}
唯一的方法是返回克隆(),以便对克隆而不是原始克隆进行任何更改吗?我知道对于数组,有一个函数以只读形式返回数组。哦,我知道这是一种参考类型。。。此外,我想知道是否有一些隐藏的C#特性来锁定书写部分

我试图提出一个通用的只读包装类,但不知道如何在不进行一些昂贵的反射之类的操作的情况下将属性获取为只读


哦,我真的在努力避免创建第二个版本的类,它都是只读属性。在这一点上,我最好返回克隆。

没有这样的功能-您已经涵盖了您的选项


克隆它或使其成为只读的
Person
类型。后一种方法通常是首选的,因为语义更清晰:调用方显然不应该(也不能)修改实例。

不。您正在寻找类似C++style
const
-ness的东西,而C#没有


但是,匿名类型确实是不可变的

没有办法使对象的所有属性都从类外部只读。在上面的示例中,除非将Person类中的属性更改为只读,否则无法将_manager属性设置为只读

您可以将Person类的属性设置器设置为内部,这意味着只有与Person相同的程序集中的类才能更改属性

public interface IPerson
{
    string FirstName { get; }
    string LastName { get; }
}
public class Person:IPerson
{
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

public class SomeClass
{
public SomeClass(Person manager)
{
    if (manager == null)
        throw new ArgumentNullException("manager");

    _manager = manager;
}

private readonly Person _manager;
public IPerson Manager
{
    get { return _manager; } //How do I make it readonly period!
}
}

或者,如果将属性的setter设置为私有,则只有Person中的代码才能更改属性的值。

为了避免创建额外的类,可以将其实现为仅具有只读属性的接口IPerson

public interface IPerson
{
    string FirstName { get; }
    string LastName { get; }
}
public class Person:IPerson
{
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

public class SomeClass
{
public SomeClass(Person manager)
{
    if (manager == null)
        throw new ArgumentNullException("manager");

    _manager = manager;
}

private readonly Person _manager;
public IPerson Manager
{
    get { return _manager; } //How do I make it readonly period!
}
}

在某些情况下,您可以借助
Castle.DynamicProxy
冻结对象(使其不可变)。阅读本博客了解详细信息。

您可以将Person类转换为不可变对象,如下所示

public class Person 
{ 
    public Person( string firstName, string lastName )
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public String FirstName { get; private set; } 
    public String LastName { get; private set; } 

} 

匿名对象是只读的。

这里是另一个示例,基于.NET Framework 2.0中如何实现List.AsReadOnly。在适当的方法中使用布尔值(IsReadOnly)来防止更新:

public class Person
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (!IsReadOnly) _firstName = value;
            else throw new AccessViolationException("Object is read-only.");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (!IsReadOnly) _lastName = value;
            else throw new AccessViolationException("Object is read-only.");
        }
    }

    internal virtual bool IsReadOnly { get { return false; } }

    public ReadOnlyPerson AsReadOnly()
    {
        return new ReadOnlyPerson(this);
    }

    public class ReadOnlyPerson : Person
    {
        private Person _person;
        internal override bool IsReadOnly { get { return true; } }

        internal ReadOnlyPerson(Person person) // Contructor
        {
            this._person = person;
        }
    }
}
要测试它,请执行以下操作:

static void Main(string[] args)
{
    Person p1 = new Person();
    p1.FirstName = "Joe";
    p1.LastName = "Bloe";
    Console.WriteLine("First = {0} Last = {1}", p.FirstName, p.LastName);

    var p2 = p1.AsReadOnly();
    p2.FirstName = "Josephine"; // AccessViolationException
}

根据评论:是否有任何已知的快速高效的“只读”通用包装器类?我试图制作的一个必须使用反射,如果可以避免的话,这并不是我真正想要做的。使用接口而不是具体类型有很好的理由(如果
Person
有任何行为,我也建议这样做)。。。但是与创建一个类相比,这又能节省多少精力呢?我也喜欢这样,但是对于他们来说,把它重新放到Person类中不是很容易吗<代码>Person-changeproperties=(Person)SomeClass.Manager而强制转换不会阻止它们使用该引用对属性进行更改。。。我可能不得不坚持克隆。不过,只读接口会很好,所以反射告诉他们不能执行setter属性。@myermian,你说得对。尽管这对于用户来说是一个糟糕的实践。将人员设置为内部可以阻止这种情况(但会阻止他们构建新的人员)。即使使用反射标记内部,他们也可以访问setter。此外,传回克隆不会阻止用户对某个类进行反思并直接获得_manager。你不能阻止人们朝自己的脚开枪。当有人开始反对你公开的API时,他们应该承担由此产生的任何后果。@jeff我相信维护人员会带来好处。如果IPerson接口发生更改,编译器将强制更新该人员以与之匹配。另外,如果使用ReadOnlyPerson类,则需要创建Person->ReadOnlyPerson的某种映射。@statenjason,我想你是对的。你只能保护用户不被“射中自己的脚”,这种方法在C#标准库中也很常见。请参见
String
/
StringBuilder
Uri
/
UriBuilder
。只有当只读用法多得多时才有意义,这两种用法都是如此。