Home > database >  Easy way to share private variables with constructed class in C#
Easy way to share private variables with constructed class in C#

Time:06-15

So I'm aware of several ways to make private fields accessible to other classes, I just I feel like their must be an easier way because I could do it easier in C with pointers.

Basically, I have a class with a large number of integers, that then creates a number of classes that use these integers. But each class only uses a few integers each. What I want is to allow each class to access only the variables that they need, but have all the remaining integers be unaccessible.

Basically what I would do in C is:

class PrivateClass
{
private:
    int a, b, c, d;
public:
    void DoStuff();
};

And then

void DoStuff()
{
    ClassOne class_one(&a, &b);
    ClassTwo class_two(&c, &d);
    //stuff
}

So then class_one has access to the values of a and b, while class_two has access to the values of c and d. (Also, if I've made any errors in my code forgive me, it has been a while since I've actually written in C )

Given how easy it is to do there, it makes me feel (perhaps erroneously) like there must be a similarly easy method in C#. If not, I'll mostly just make an indexer to encapsulate the variables so that they can be accessed that way.

Edit: Basically I'm getting that I should just use properties like I've been weirdly avoiding. I don't mind making properties for a few fields, but for some reason doing it for 30 or so just feels wrong, like there should be a better way.

CodePudding user response:

The idea of sharing private variables with another class doesn't quite make sense. There are various access modifiers, but that doesn't help if you want one class to have access to certain members, another class to have access to different members, and so forth.

So the first question is how to share variables. Typically we do that with properties.

In this example another class can read A but can't change it. Another class can both read and write B. There is no property for changing _c. If we want to split hairs, nothing outside the class can actually read or change the variables. They can only access the property, and the property reads/writes the variable. (You can also do this with auto-properties. You don't need a variable and a separate property. But that's irrelevant for now.)

class PrivateClass
{
    private int _a;
    private int _b;
    private int _c;

    public int A => _a;
    public int B
    {
        get { return _b; }
        set { _b = value; }
    }
};

Next you want to be able to control which classes can "see" which variables. As long as there are properties, you can't absolutely prevent someone from writing code that calls them. But you can control how one class "sees" another class.

Here's a contrived example. These types don't make much sense.

public class PublicTransportVehicle : IPublicTransport, IMotorVehicle
{
    public int PassengerCapacity { get; private set; }
    public int PassengerCount { get; set; }
    public int AxleCount { get; private set; }
}

public interface IPublicTransport
{
    int PassengerCapacity { get; }
    int PassengerCount { get; set; }
}

public interface IMotorVehicle
{
    int AxleCount { get; }
}

The PublicTransportVehicle class has three int properties. Two are read-only. One is read-write. (I used auto-properties instead of variables. This just means that if set is private then only the class itself can set the property.)

Now I can write a class with a method that takes an argument of type IPublicTransport. I can pass an instance of PublicTransportVehicle or any other class that implements the interface. But the method only sees IPublicTransport. The only properties it knows about are the ones exposed by that interface.

I can write another method that takes an argument of type IMotorVehicle and it only interacts with the properties defined in that interface.

I can pass an instance of PublicTransportVehicle to either method because it implements both interfaces, but each one sees it differently.


Can we absolutely prevent the caller from accessing properties we didn't want it to access? That's a lot harder. For example, a class could do this:

public void DoSomethingWithMotorVehicle(IMotorVehicle motorVehicle)
{
    var publicTransportVehicle = motorVehicle as PublicTransportVehicle;
    if (publicTransportVehicle != null)
    {
        publicTransportVehicle.PassengerCount = 1000;
    }
}

But we usually can't and shouldn't bother trying to exercise complete control over that. It's practically impossible. The idea is to communicate which properties and methods consumers are expected to interact with. We do that by deliberately making them accessible. If someone wants to something weird like casting an object or using reflection there's not a lot we can do. Unless it's a serious security-related matter we don't need to worry about that.

CodePudding user response:

Private fields, methods, properties, constructors, and events are all meant to be used by the class only. If you want to access these fields from other classes, you can make something like this:

using System;

namespace SharePrivateFields
{
    class Supervisor
    {
        void DoStuff()
        {
            var subject = new Subject();
            var first = new First(subject);
            var second = new Second(subject);
        }

    }

    class Subject : IFirstSubject, ISecondSubject
    {
        public int A { get; set; }
        public int B { get; set; }
        public int C { get; set; }
        public int D { get; set; }
    }

    interface IFirstSubject
    {
        int A { get; set; }
        int B { get; set; }
    }
    interface ISecondSubject
    {
        int C { get; set; }
        int D { get; set; }
    }

    class First
    {
        private IFirstSubject _subject;

        public First(IFirstSubject subject)
        {
            _subject = subject;
        }

        protected void DoMagic()
        {
            Console.WriteLine(_subject.A); // Completely correct
            Console.WriteLine(_subject.C); // `IFirstSubject` does not contain definition for `C`
        }
    }

    class Second
    {
        private ISecondSubject _subject;

        public Second(ISecondSubject subject)
        {
            _subject = subject;
        }
        protected void DoMagic()
        {
            Console.WriteLine(_subject.A); // `ISecondSubject` does not contain definition for `A`
            Console.WriteLine(_subject.C); // Completely correct
        }
    }
}

However, we're now treading waters of Abstract Factory Design Pattern, which is a more desirable approach in this case.
I'm convinced that the C implementation you're mentioning is a flawed design, so it's a reason why you can't make it easy in C# (if we're being completely honest - you can, read about Unsafe code, pointer types, and function pointers; but it's 99% of a time taboo for C# code).

So I suggest you learn about Abstract Factory or at least use my solution for your use-case.

CodePudding user response:

Make the method static and send the corresponding instance to it, then you can access the data you want.

Static Void DoStuff(PrivateClass instance)
{
    ClassOne class_one(instance.a, instance.b);
    ClassTwo class_two(instance.c, instance.d);
    //stuff
}

But the reality is that it is wrong, you should either add reading and writing properties to your variables to be able to send them to DoStuff from outside as a parameter.

//in Main
PrivateClass pClass = new PrivateClass(1,30,2,19);
DoStuff(pClass.A, pClass.B, pClass.C, pclass.D);

//in PrivateClass

namespace Test
{
    class PrivateClass
    {
        private int a;
        private int b;
        private int c;
        private int d;

        public PrivateClass(int a, int b, int c, int d)
        {
            this.a = a;
            this.b = b;
            this.c = c;
            this.d = d;
        }

        public int A
        {
            get
            {
                return this.a;
            }
            set
            {
                this.a = value;
            }
        }//etc
        
        public static void DoStuff(int a, int b, int c, int d)
        {
            ClassOne class_one(a, b);
            ClassTwo class_two(c, d);
            //stuff
        }
    }
}

  •  Tags:  
  • c#
  • Related