C# References; Keeping Members Hidden
Asked Answered
B

5

5

Imagine you have a class defined as follows.

public class SomeClass
{
     public Manager m { get; protected set; }
     public SpecialData data { get; protected set; }

     //More methods and member code here...
}

I need my manager class to somehow be able to change the set reference for the SpecialData member. I could do this with a double pointer or friend classes in C++, but that option isn't available in C#, sadly. How do I keep SpecialData protected from outside users setting it, while still allowing the Manager class control over setting? I could do this with internal keyword, but that doesn't seem incredibly safe or clean...

Any help is greatly appreciated.

Balsamiferous answered 18/9, 2014 at 14:20 Comment(1)
Could you explain why you believe internal is unsafe or unclean? Do you mean that it is so in general, or are you suggesting it's not suited to your specific design? I would say it's a perfect solution here, though truth be told I'm not sure why you want to do this in the first place.Dwight
D
4

If the manager class is part of the same assembly as SomeClass, making the member internal would make it possible for classes in the same assembly to access the setter:

public SpecialData data { get; protected internal set; }

This is similar to using friend in C++, with the exception that the "friendship" is extended to all members of the same assembly.

If the manager is part of a different package, you can use InternalsVisibleTo attribute on your assembly. In this case, however, you should sign the assembly to which you extend friendship to avoid attempts to gain access to the setter from unauthorized code.

Dyanne answered 18/9, 2014 at 14:23 Comment(4)
I did mention the internal keyword. I realize it's an option, but is it a good one? I was worried that someone it might be a design flaw. If not, this would certainly work. This is a big project, so coupling these together could be a massive issue...Balsamiferous
@user3812869 Yes, internal is a good option. It is designed for situations like this, when you design a package of related classes that share knowledge of internal representation. .NET takes precautions not to leak this knowledge outside your library. Presumably, there is no way to protect your code from people who can modify your assembly.Dyanne
Well in that case I'll try to work out the kinks. The design just seems slightly odd to me. In reality I have a StateManager and a Sprite contained in a game object. The StateManager contains States. These States need to edit the Sprite field.Balsamiferous
@user3812869 The last bit does sound a little odd, because it sounds like Sprite is part of visual representation while everything else is part of a model. You may be better off changing sprite identifiers of sorts, and "link" them to Sprite objects on the view level. This does not change the way you set up accessibility in your classes, though.Dyanne
P
3

What about creating an event on the Manager class, something like a RequestChangeSpecialData event. The Manager fires the event, and the SomeClass will change the SpecialData instance.

public class SomeClass
{
     private  Manager _m;

     public Manager M 
     { 
        get { return _m} 
        set 
        {
            // register/unregister event on property assignment
            if(_m != null)
                _m.RequestChangeSpecialData -= RequestChangeSpecialData;

            _m = value;

            if(_m != null)
                _m.RequestChangeSpecialData += RequestChangeSpecialData;

        }
     }

     public SpecialData Data { get; private set; }

     private void RequestChangeSpecialData(object sender, ChangeSpecialDataEventArgs e)
     {
        // set the new reference
        Data = e.SpecialData;
     }

}

public class Manager
{
    public void DoSomething()
    {
        // the manager class wants to do something, and wants to change the SpecialData instance.., so it fires the event RequestChangeSpecialData


        SpecialData data = new SpecialData();

        // if the event is bound.
        if(RequestChangeSpecialData != null)
            RequestChangeSpecialData(this, new ChangeSpecialDataEventArgs(data));
    }

    public event EventHandler<ChangeSpecialDataEventArgs> RequestChangeSpecialData;
}

public class ChangeSpecialDataEventArgs : EventArgs
{
    public SpecialData Data {get; private set; }

    public ChangeSpecialDataEventArgs(SpecialData Data)
    {
        Data = data;
    }
}

UNTESTED (wrote in notepad)

Now the Manager is able to change the SpecialData property. This way the manager is not dependent from the SomeClass/interface or assembly.

Polytheism answered 18/9, 2014 at 14:28 Comment(1)
was about to post event optionLucielucien
S
1

Create a class that inherits SomeClass and looks kind of this:

internal class SomeClassDecorator : SomeClass
{
    public void SetSpecialData(SpecialData value)
    {
        data = value;
    }
}

Protected means that it's available to the derivatives of the class, and since SomeClass is not sealed, you can simply derive from it and do whatever you need. You then can use this decorator instead of SomeClass itself. And moreover, you can have as much decorators as you need, each handling its special SpecialData.

Sinhalese answered 18/9, 2014 at 14:23 Comment(0)
V
1

It might not be exactly what you are looking for, but the internal keyword described here can govern access within the same assembly; it seems a similar purpose as the friend keyword in C++.

Vaud answered 18/9, 2014 at 14:23 Comment(0)
B
1

You could make the property internal. That will make the property only visible inside the same assembly. Optionally you could use the InternalsVisibleToAttribute to allow specific assemblies access to that property.

Another approach is the use of an interface to hide that property:

public interface ISomeClass
{
     Manager m { get; }
     SpecialData data { get; set; }
}

public class SomeClass : ISomeClass
{
     public Manager m { get; protected set; }
     SpecialData ISomeClass.data { get; set; }

     //More methods and member code here...
}

In this way, data is only visible from interface reference.

So this doesn't work:

SomeClass c = new SomeClass();
c.data;

But this does:

ISomeClass c = new SomeClass();
c.data;
Bowel answered 18/9, 2014 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.