限制类成员的作用域超出私有范围

3

我在想是否有一种方法可以仅将c#类成员的访问范围限定于get/set实现,以减少我意外直接访问它们的机会。类似于private但仅允许get/set访问它,我考虑将每个变量都包装在自己的类中,但这似乎对我寻找的限制来说太过杀鸡焉用牛刀了。谢谢


除了get和set之外,还有其他类型的访问方式吗? - GolezTrol
在大多数情况下,你肯定需要对特定的类成员进行更多的操作,而不仅仅是设置和获取它吧?它必须以某种方式影响对象的行为,不是吗? - Oliver Charlesworth
如果你觉得需要保护一个私有变量,防止某些类方法访问它,那么我会说这表明你的类具有多个职责。 - Anders Abel
@Anders Abel - 这很可能是我的问题。我认为我有一个倾向,让单个类处理比它们应该处理的更多内容。这可能是我未来需要改进的东西。 - hookeslaw
4个回答

4

您可以在字段中添加[Obsolete("使用属性")],并通过编写#pragma warning disable 618来抑制属性中的警告。


3
不好意思,没有。我猜你可能需要类似这样的内容:

不,很遗憾没有。我猜想你可能需要像这样的东西:

// Not actually valid C#!
public string Name
{
    // Only accessible within the property
    string name;

    get { return name; }
    set
    {
        if (value == null)
        {
            throw new ArgumentNullException();
        }
        name = value;
    }
}

我之前也想过类似的事情,但很遗憾这是不可能的 :(


但我们可以使用自动实现属性来实现类似的功能。至少我们不需要变量来存储值。然而,我们也无法指定我们的get set实现。 - Shekhar_Pro
如果在属性的上下文中有某种“field”关键字,它将是对属性后备字段的引用,那将是很好的。或者也许这个解决方案已经足够好了?我最近观看了您在NDC上关于C#5想法的演讲 - 真的很酷的想法。 - TheCloudlessSky
@TheCloudlessSky:这是一种可能性,但它会限制表示为单个字段。如果出于某种原因需要两个字段怎么办?(例如,两个单独的值组成一个点)。顺便说一下,我不认为这很快会发生。 - Jon Skeet
是的,那是一个非常好的观点。我希望有更多像F#这样支持不可变性的支持,但我怀疑那会发生。 - TheCloudlessSky
使用lambda表达式时,我偶然发现了一个有趣的可能性。 :) 我在我的更新答案中进行了澄清。 (https://dev59.com/9lTTa4cB1Zd3GeqPq0nq#4986143) - Steven Jeuris

2

这是不可能的。唯一的访问修饰符包括privateinternalprotectedprotected internalpublic,但没有一个适合此情况。

然而,如果您使用自动属性,那么当然只能通过get和set来访问它。


1

如果您在访问器中不需要执行任何操作,请使用自动实现属性

我猜您很可能确实想要执行操作,这就是为什么您想要确保使用属性而不是后备字段的原因。

在这种情况下,请考虑以下内容:

  • 使用命名约定,如“_instanceName”来指示私有成员字段。(无论如何,您都应该这样做...)
  • 当您感觉访问器内部的操作是常见的且可重用的时候,请将其封装在一个类中。在遇到性能问题之前不必担心过度设计。

我相信我可能已经找到了一个可能的解决方法,而且它非常简单。然而,这个“解决方案”可能有点太聪明了。也许明天我会进行一些基准测试。问题是目前它也被限制在每个使用它的地方,也许通过使用泛型可以将其限制。

它利用了lambda始终具有相同的支持方法这一事实。通过将lambda传递给静态构造函数,静态对象可以跟踪此唯一的“作用域”并将变量链接到它。有关此实现的更多详细信息可以在此处找到

用法:

class LocalTestClass
{
    public int StaticTest( int setValue )
    {
        Local<int> test = Local<int>.Static( () => { } );
        int before = test.Value;
        test.Value = setValue;
        return before;
    }

    public int InstanceTest( int setValue )
    {
        Local<int> test = Local<int>.Instance( () => this );
        int before = test.Value;
        test.Value = setValue;
        return before;
    }
}

[TestMethod]
public void LocalStaticTest()
{
    LocalTestClass instance1 = new LocalTestClass();
    LocalTestClass instance2 = new LocalTestClass();

    instance1.StaticTest( 10 );
    Assert.AreEqual( 10, instance2.StaticTest( 20 ) );
    Assert.AreEqual( 20, instance1.StaticTest( 30 ) );
}

[TestMethod]
public void LocalInstanceTest()
{
    LocalTestClass instance1 = new LocalTestClass();
    LocalTestClass instance2 = new LocalTestClass();

    instance1.InstanceTest( 10 );
    Assert.AreEqual( 10, instance1.InstanceTest( 20 ) );
    instance2.InstanceTest( 50 );
    Assert.AreEqual( 20, instance1.InstanceTest( 30 ) );
}

这个类:

public class Local<TValue>
{
    static readonly Dictionary<object, object> StaticScope
        = new Dictionary<object, object>();
    static readonly Dictionary<object, Dictionary<object, object>> InstanceScope
        = new Dictionary<object, Dictionary<object, object>>();


    public TValue Value { get; set; }


    private Local() { }


    public static Local<TValue> Static( Action scope )
    {
        if ( !StaticScope.ContainsKey( scope ) )
        {
            Local<TValue> newInstance = new Local<TValue>();
            StaticScope.Add( scope, newInstance );                
        }

        return StaticScope[ scope ] as Local<TValue>;
    }

    public static Local<TValue> Instance<TScope>( Func<TScope> scope )
    {
        object instance = scope();
        if ( !InstanceScope.ContainsKey( instance ) )
        {                
            InstanceScope.Add( instance, new Dictionary<object, object>() );

            if ( !InstanceScope[ instance ].ContainsKey( scope ) )
            {
                Local<TValue> newInstance = new Local<TValue>();
                InstanceScope[ instance ].Add( scope, newInstance );
            }
        }

        return InstanceScope[ instance ][ scope ] as Local<TValue>;
    }
}

关于这个话题的更广泛讨论可以在Programmers.SE上找到

我意识到实例作用域变量在实例不再使用后不再进行垃圾回收。我正在尝试使用弱引用来寻找解决方案。 - Steven Jeuris

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