Only allow certain classes to edit certain properties - c#

I have a class with a PictureBox created as followed:
public class Tile
{
public PictureBox tilePB = new PictureBox(); //properties don't matter in this case
}
I also have a class GameManager. This is like a referee.
I want to make it so the BackColor of Tile.tilePB can only be edited by Gamemanager and nothing else, and no other class.
I currently have a public PictureBox for Gamemanager (to edit) and a public get function for other classes, but I want to actually make this a valid system instead of what I have right now.
Is this even possible? Please include explenation for the required code.
EDIT: I ran into an issue that I hadn't thought off: class Gamemanager is a static class. I do everything in that class via public static functions. Is this still possible? Since this doesn't work.

You can't do this at compile time, but it can be done at runtime:
public class PictureBox
{
private Color _backColor;
public void SetBackColor(Color color)
{
//getting class type that called this method
var stackTrace = new StackTrace();
var stackFrames = stackTrace.GetFrames();
var callingFrame = stackFrames[1];
var method = callingFrame.GetMethod();
//checking if the class type is GameManager
if (!method.DeclaringType.IsAssignableFrom(typeof(GameManager)))
{
throw new FieldAccessException("Only GameManager can set the background color of a PictureBox!");
}
_backColor = color;
}
public Color BackColor => _backColor;
}
public class Tile
{
public PictureBox tilePB { get; set; }
}
//example GameManager class
public class GameManager
{
public void SetBackground()
{
var someTile = new Tile()
{
tilePB = new PictureBox()
};
var someColor = new Color();
someTile.tilePB.SetBackColor(someColor);
}
}
//example class that may want to set picturebox background color
public class MaliciousClass
{
public void SetBackground()
{
var someTile = new Tile()
{
tilePB = new PictureBox()
};
var someColor = new Color();
someTile.tilePB.SetBackColor(someColor);
}
}
Then somewhere:
var gm = new GameManager();
var mc = new MaliciousClass();
gm.SetBackground(); //this is fine
mc.SetBackground(); //this will throw an exception
If you don't want to throw an exception or you want to do something different when "not authorized" class is trying to access the SetBackColor method then just replace throw new FieldAccessException() with return or whatever you want.
Bare in mind the approach presented here is inefficent and it just presents that in can be done at runtime and nothing more than that.

Not sure if this is exactly what you are looking for, but I made this quick test and it seems to be able to differentiate the calling class:
class Program
{
static void Main(string[] args)
{
Type1 something1 = new Type1();
Type2 something2 = new Type2();
something1.runTest();
something2.runTest();
Console.ReadKey();
}
public class Type1
{
public void runTest()
{
Testing.edit(this);
}
}
public class Type2
{
public void runTest()
{
Testing.edit(this);
}
}
public static class Testing
{
public static void edit(object obj)
{
// This is where you test the calling class to make sure
// it is allowed to edit.
Console.WriteLine(obj.GetType().ToString());
}
}
}

The only way I can think of where you enforce this at compile time, end up being a bit complicated. I don't think you'll want to do this.
You can create an interface with properties/methods for everything that only the GameManager is allowed to do. You can implement this interface in a private inner class below Tile, and make sure the only way this object is created is by passing in a GameManager that receives it. Now, the only way the access can 'leak' is if the GameManager 'gives away' the object.
public class GameManager {
public void AddTile(Tile t, Tile.IManagerHook m) {
m.SomeProperty = "set from manager";
}
}
public class Tile
{
public object SomeProperty { get; private set; }
public Tile(GameManager manager) {
manager.AddTile(this, new ManagerHook(this));
}
public interface IManagerHook {
object SomeProperty {get; set;}
}
private class ManagerHook : IManagerHook {
private Tile _tile;
public ManagerHook(Tile t) {
_tile = t;
}
public object SomeProperty {
get{ return _tile.SomeProperty;}
set { _tile.SomeProperty = value; }
}
}
}

(seems) Simply not possible
After asking several programmers, the way I have coded everything and what I want seems to be simply impossible without immensely complicated code - to the point you are better off refacturing everything. Since class Gamemanager is a static class, there will be no instances of it so you can not check if the 'object' that called it is of class Gamemanager. this also doesn't work since Gamemanager is, agian, static.

Related

How to know when field or property initialization is done?

For example, there's two classes here.
Class One:
using UnityEngine;
using System.Collections.Generic;
public class One:MonoBehavior
{
private List<string> _assetBundleList;
private void Start()
{
InitializeList();
}
private void InitializeList()
{
//Add assetBundleList field some value.
}
public IEnumerator<string> GetEnumerator()
{
return _assetBundleList.GetEnumerator();
}
}
Class two:
public class Two:MonoBehavior
{
public GameObject gameObjectWithScriptOne;
private One _scriptOne;
private void Start()
{
scriptOne = gameObjectWithScriptOne.GetComponent<One>();
DoSomething();
}
private void DoSomething()
{
foreach(var assetBundle in scriptOne)
{
//Load asset
}
}
}
Script one is just like a manager things, I use this for storing asset bundle data, perhaps the list value will change. Script two must wait for initializing done. Is there any way to wait for it except adjusting script order?
It can be handled with easily creating a context that initialize things sequentially. In short you need to block the main thread.
Let's say you have a root or persistent scene that do must things like loading assets etc.
//The only object in the Root Scene.
public class RootContext : MonoBehaviour
{
private const string _playerAddress = "Player";
public void Awake()
{
//Load assets
//wait for assets loading
//instantiate things.
//new instantiated things can also initalize with this way.
var handle = Addressables.LoadAssetAsync<GameObject>(_playerAddress);
var asset = handle.WaitForCompletion();
var go = Instantiate(bla bla bla);
}
}
I think the what you want is a framework that organize things. You can look at Strange. It is MVCS + IoC framework. Addionality what you said about "when field or property initialization is done" with [PostConstruct] attribute with Strange.
[Inject(ContextKeys.CONTEXT)]
public IContext Context { get; set; }
/*Initialization of injections are guaranteed at here. Their initialization
is also quaranteed like this class.
*/
[PostConstruct]
public void Initalize()
{
}
I'd go with refactoring, but if this is not the case C# events might be the solution.
Class One
public class One:MonoBehavior
{
private List<string> _assetBundleList;
public event Func Initialized;
private void Start()
{
InitializeList();
}
private void InitializeList()
{
//Add assetBundleList field some value.
Initialized?.Invoke();
}
public IEnumerator<string> GetEnumerator()
{
return _assetBundleList.GetEnumerator();
}
}
Class Two:
public class Two:MonoBehavior
{
public GameObject gameObjectWithScriptOne;
private One _scriptOne;
private void Start()
{
scriptOne = gameObjectWithScriptOne.GetComponent<One>();
scriptOne.Initialized += DoSomething;
}
private void DoSomething()
{
foreach(var assetBundle in scriptOne)
{
//Load asset
}
}
}
Also, you should unsubscribe from events when disabling an object, but you'll figure it out by yourself.

Encapsulating inner class and acessing outside in C#

I have these classes, one is a model, other is Listener and the third one is an Util class. I want to access Terrains by the variable map in the first one, but don't want public access to the inner class Terrain. Is there any way to do it?
It prints error CS0052: Inconsistent accessibility: field type
System.Collections.Generic.List is less
accessible than field `MapaMundiInfoScript.map'
public class MapaMundiInfoScript : MonoBehaviour {
public static bool changeInMap= false;
public static List<Terrain>map = new List<Terrain>();
void Start(){
Terrain terrain = new Terrain(0,0);
Terrain.TerrainPart initialPart = new Terrain.TerrainPart(20,20,0,0);
terrain.terrainParts.Add (initialPart);
map.Add(terrain);
changeInMap=true;
}
class Terrain{
int XPosition;
int ZPosition;
public List <TerrainPart> terrainParts = new List<TerrainPart> ();
public Terrain(int XPosition, int ZPosition){
this.XPosition=XPosition; this.ZPosition=ZPosition;
}
public class TerrainPart
{
int XSize;
int ZSize;
int XPosition;
int ZPosition;
TerrainPartReturn ReturnTerrainPart(int num1,int num2,int num3,int num4)
{
return new TerrainPart (num1,num2,num3,num4);
}
public TerrainPart(int XSize,int ZSize,int XPosition,int ZPosition){
this.XSize = XSize;
this.ZSize = ZSize;
this.XPosition=XPosition;
this.ZPosition =ZPosition;
}
}
}
public class MapListener : MonoBehaviour {
void Update () {
if (MapaMundiInfoScript.changeInMap) {
foreach(MapaMundiInfoScript.Terrain terrain in MapaMundiInfoScript.mapMundi)
{
foreach(terrain.terrainPart terrainPart in terrain.terrainParts)
{
RegionDraw.Draw(terrainPart);
}
}
MapaMundiInfoScript.changeInMap = false;
}
}
public class RegionDraw
{
/***
Implementantion Draw Method
***/
}
You cannot reference a private class as a public property. You will need to have the class public for public access. Consider making your properties and methods private, private protected, internal etc.
If you need to provide read only attributes, you can use public getters and private setters, etc. If you need to prevent the execution of some methods consider setting those to private, etc. The class can be public while still locking down properties and methods inside the class. Consider what it is that you actually need to expose.
You could also expose the functionality of these hidden classes through interfaces
public interface ITerrain
{
List<ITerrainPart> TerrainParts { get; }
ITerrainPart CreateTerrainPart(int XSize, int ZSize, int XPosition, int ZPosition);
}
public interface ITerrainPart
{
// ...
}
Implement them like this
private class Terrain : ITerrain
{
int XPosition;
int ZPosition;
public List<ITerrainPart> TerrainParts { get; } = new List<ITerrainPart>();
public Terrain(int XPosition, int ZPosition)
{
this.XPosition = XPosition; this.ZPosition = ZPosition;
}
public ITerrainPart CreateTerrainPart(int XSize, int ZSize, int XPosition,
int ZPosition)
{
return new TerrainPart(XSize, ZSize, ZPosition, ZPosition);
}
private class TerrainPart : ITerrainPart
{
// ...
}
}
Your listener can then draw like this (after changing the parameter type of Draw to ITerrainPart):
void Update()
{
if (MapaMundiInfoScript.changeInMap) {
foreach (ITerrain terrain in MapaMundiInfoScript.map) {
foreach (ITerrainPart terrainPart in terrain.TerrainParts) {
RegionDraw.Draw(terrainPart);
}
}
MapaMundiInfoScript.changeInMap = false;
}
}
Let MapaMundiInfoScript have a method DrawTerrain() and let Terrain have a method DrawParts. Should you end up with to many incoherent methods in MapaMundiInfoScript, you might want to use a visitor.

Run method from different scope

I know that what i need help with is not called the Parent but that was the closest word i could think of to describe this situation.
This is not actual code i'm using.
I have MainClass which contains object ObjectA.
class MainClass
{
public int mode = 0;
ObjectA obj = new ObjectA();
}
I need to access the mode variable from the ObjectA object. Is this possible? I feel like if i could at least call a method in MainClass from obj i would be all set. Also I'm aware calling MainClass a parent in this sense is incorrect, what is the correct term for both the MainClass and obj in this.
Assuming you are able to change MainClass, I can think of two options:
Add a reference to MainClass when you create an ObjectA instance.
Like you said in your comments, use events.
Using a reference:
class MainClass
{
public int mode = 31416;
ObjectA obj;
public MainClass()
{
obj = new ObjectA(this);
}
public int GetMainClassMode()
{
return mode;
}
public void Test() {
Console.WriteLine("Calling test method inside obj");
obj.Test();
}
}
class ObjectA {
MainClass parent = null;
public ObjectA(MainClass parent)
{
this.parent = parent;
}
public void Test()
{
if (parent != null)
{
Console.WriteLine("Getting mode from 'parent' MainClass");
Console.WriteLine(string.Format("Mode = {0}", parent.GetMainClassMode()));
}
}
}
Using events:
class MainClass
{
public int mode = 31416;
ObjectA obj = new ObjectA();
public MainClass()
{
obj.ValueReturnEvent += HandleValueReturnEvent;
}
public int GetMainClassMode()
{
return mode;
}
// Handle event, return data
private int HandleValueReturnEvent(object sender, EventArgs e)
{
return mode;
}
public void Test() {
Console.WriteLine("Calling test method inside obj");
obj.Test();
}
}
class ObjectA {
// delegate
public delegate int ReturnValueEventHandler(object sender, EventArgs args);
// event
public event ReturnValueEventHandler ValueReturnEvent;
public void Test()
{
// make sure at least one subscriber
if (ValueReturnEvent != null)
{
// note the event is returning a value
var myValue = ValueReturnEvent(this, null);
Console.WriteLine("Getting mode from 'parent' MainClass");
Console.WriteLine(string.Format("Mode = {0}", myValue));
}
}
}
On both cases, you get this output:
Calling test method inside obj
Getting mode from 'parent' MainClass
Mode = 31416
Even though you've got your answer, you could also simply inject it into your ObjectA ... have a constructor that takes an int, and when you create the object pass the mode in and save it in that object.
I find it cleaner that the object uses whatever it needs from its scope, rather than accessing the parent to ask for a variable.
I'm not saying it might not be needed, it just another thought.
You'll have to wire things up to let the child know about the parent for this to work. Something like this:
class ParentClass
{
public int mode = 0;
public ChildClass child = null;
public ParentClass()
{
child = new ChildClass(this);
}
}
class ChildClass
{
public readonly ParentClass parent = null;
public ChildClass (ParentClass parent)
{
this.parent = parent;
}
public int MethodThatReadsParentMode()
{
int mode = parent.mode;
return mode;
}
}
You make field mode public, so I assume it's possible that someone who uses class MainClass do can change this field.
Let's assume program looks like this.
class Program {
var main = new MainClass();
main.mode = 1;
}
Your obj field is private, so Program cannot access it.
So, when someone changed field mode, ObjectA should get new value of field mode.
Possible solution:
class ObjectA {
public int Mode { get; set; }
}
class MainClass {
private obj = new ObjectA();
public int Mode {
get { return this.obj.Mode; }
set { this.obj.Mode = value; }
}
}
There is another option in case field mode belongs to MainCalss.
class ObjectA {
private int mode;
public ObjectA(int mode) {
this.mode = mode;
}
// you can create property instead of method
// I'm not sure how you use this variable, so I just added set method
public void SetMode(int mode) {
this.mode = mode;
}
}
class MainClass {
private int mode = 0;
private obj = new ObjectA();
public int Mode {
get { return this.mode; }
set {
this.obj.SetMode(value);
this.mode = value;
}
}
}

One class makes instances of another class. These instances need to use information in original class, how to organize?

I'm fairly new to object oriented programming and I still have trouble organizing my programs.
I'm making a simulation of a factory in c#. I have one instance of a Simulation class. When initialized, the constructor creates several instances of a Machine class.
The Simulation class has an event list. The Machine class has methods that need to add events to this list. Right now, the only way I see how to do this is to pass a reference of the simulation to the Machine constructor. However, these seems messy to me and I feel like there has got to be a better way of organizing this.
public class Simulation
{
public LinkedList<Event> event_list = new LinkedList<Event>();
public Simulation()
{
Machine m1 = new Machine(this);
Machine m2 = new Machine(this);
}
static void Main(string args[])
{
Simulation sim = new Simulation();
}
}
public class Machine
{
public Simulation sim;
public Machine(Simulation sim)
{
this.sim = sim;
}
public void Schedule_Event()
{
sim.event_list.AddLast(new Event(etc etc));
}
}
public class Event
{
}
Is there a way to add events to Simulation.event_list without having to pass the this reference? In a sense each Machine "belongs" to the Simulation class, and I want to show this somehow.
You can have a method in Machine that returns Event objects, then Simulation can take care of adding it to event_list:
public class Simulation
{
public LinkedList<Event> event_list = new LinkedList<Event>();
public Simulation()
{
Machine m1 = new Machine();
Machine m2 = new Machine();
event_list.AddLast(m1.GetEvent());
event_list.AddLast(m2.GetEvent());
}
static void Main(string args[])
{
Simulation sim = new Simulation();
}
}
public class Machine
{
public Machine() {}
public Event GetEvent()
{
return new Event(etc etc);
}
}
seems pretty much ok to me, I'd just hide the event_list implementation in the Simulation class:
public class Simulation
{
private LinkedList<Event> event_list = new LinkedList<Event>();
public void addEven(Event x) {event_list.Add(x); }
}
passing this in constructor is ok, if you really want to you can put a factory method in simulation:
public class Simulation
{
public Machine CreateNewMachine() { return new Machine(this); }
}
to just allow Machine m1 = Simulation.CreateNewMachine()
You could use a more Observer-pattern type approach, using Action<T>
Add a public action to Machine:
public class Machine
{
public Action<Event> EventAdded;
private void Schedule_Event()
{
if(EventAdded != null)
EventAdded(new Event());
}
}
And Subscribe to the action upon creation:
public class Simulation
{
public LinkedList<Event> event_list = new LinkedList<Event>();
public Simulation()
{
Machine m1 = new Machine() { EventAdded = AddEvent };
}
static void Main(string[] args)
{
Simulation sim = new Simulation();
}
public void AddEvent(Event e)
{
event_list.AddLast(new Event(etc etc));
}
}
Then, the only contract you have between your two objects is the Event type.

Call method from class in class

I have no clue how to write a title correctly but what I mean is to make something like this:
public static MusicPlayer _Player = new MusicPlayer();
_Player.Play.Song(TestPath);
where MusicPlayer is a class and in that class I want to make something like property or another class I don't know how to call it, which will have two methods. My code for now:
public class MusicPlayer
{
//Variables, Methods and Properties in MusicPlayer
//And then Play which can have two tipes of play.
public static class Play
{
//This one should be called if I want to play one song
public static void Song(String _path)[...]
//And this one when I want to play from list, defined in MusicPlayer class
public static void List()[...]
}
}
You should do this:
public class MusicPlayer
{
public class Player
{
public static void Song(String _path)[...]
public static void List()[...]
}
private Player m_player = new Player();
public Player Play
{
get { return m_player; }
}
}
This defines a Player class in MusicPlayer. Also it creates a member variable of type Player and a property that allows you to access the Player instance from the outside using an instance of MusicPlayer:
var mplayer = new MusicPlayer();
mplayer.Play.Song(...);
If you do not want to create an instance of MusicPlayer, you could also make this static:
public class MusicPlayer
{
public class Player
{
public static void Song(String _path)[...]
public static void List()[...]
}
private static Player m_player = new Player();
public static Player Play
{
get { return m_player; }
}
}
You can now use MusicPlayer.Play.Song(...) without having to create an instance.
You can do something like that
public class MusicPlayer
{
public MusicPlayer()
{
Play = new Play();
}
public Play Play { get; private set; }
}
public class Play
{
//This one should be called if I want to play one song
public void Song(String _path){}
//And this one when I want to play from list, defined in MusicPlayer class
public void List() { }
}
And then using like
new MusicPlayer().Play.Song("");

Categories