2

So this is kind of an odd question, is there any way to modify a variable's visibility depending on a certain condition (e.g. via some attribute)?

This may be more of a design pattern question, so allow me to explain my situation:

I have a class which has many user configurable values (9 total, 4 of which are conditional). However, some of these variables only apply if certain conditions are met. Right now, they are all visible to the user. I'm looking for a way I can restrict the visibility of certain variables at compile time in a per-scope context. I want to avoid getting the user confused and having them potentially set certain values that will just get ignored.

Example:

Property B only applies if property A is true. If the user sets A to false, the current scope will lose visibility of B.

var settings = new Settings() {
    A = true,
    B = ... //Everything is fine since A is true
}


var settings = new Settings() {
    A = false,
    B = ... //Compile Error, Settings does not contain definition for "B"
}

//Somewhere that uses the settings variable...
if(A) { useB(B); } else { useDefault(); }

Is there a better solution to this than "good documentation?"

anon
  • 543
  • 4
  • 15
  • Other than https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives/preprocessor-if, not really. – mjwills Jul 18 '18 at 01:11
  • Yeah, I guess that would be the closest thing I can think of as well, however the major difference is they require a constant value opposed to being dependent on some variable or a set of variables. – anon Jul 18 '18 at 01:14
  • That's because it's a compile-time option. You can't modify visibility at runtime as far as I know. – ProgrammingLlama Jul 18 '18 at 01:22
  • @deleted : I'm looking for compile time dynamic scope. If another dev can't even read the docs then I don't think adjusting the scopes would help too much. Figured it was not possible (although cool in concept) but since there's always something new to learn I thought it couldn't hurt to ask and see if C# has something clever up its sleeve. – anon Jul 18 '18 at 01:30
  • 1
    OP, this question makes no sense. You want to modify compile-time scope based on something that happens later at runtime? How would that work? If someone writes code that accesses `B`, but hasn't run the code yet, do you expect the code to compile? Then, when the variable is changed during run-time, what happens? Do you want the application to suddenly stop working? It's not like the code erases itself when it becomes invalid. – John Wu Jul 18 '18 at 01:43
  • The variables would need to be `const` and initialized only once. If you access B, it would be invalid at compile time. The variables cannot be changed during run-time. Something like preprocessor statements but for const variables. I agree it's a bit of an odd question, but C# has surprised me in the past. I realize there is no feasible way do exactly this. It was more so to see if anything could get close. – anon Jul 18 '18 at 02:04

2 Answers2

3

You can't do exactly what you're asking, but you can get something close chaining fluent APIs with the builder pattern...

public interface ISettings
{
    string SomePropertyOnlyForTrue { get; }
    int B { get; }
}

public interface IBuilderFoo
{
    IBuilderFooTrue BuildFooTrue();
    IBuilderFooFalse BuildFooFalse();
}

public interface IBuilderFooTrue
{
    IBuilderFooTrue WithSomePropertyOnlyForTrue(string value);
    ISettings Build();
}

public interface IBuilderFooFalse
{
    IBuilderFooFalse WithB(int value);
    ISettings Build();
}

public void Whatever()
{
    var theThingTrue = new BuilderFoo().BuildFooTrue()
        .WithSomePropertyOnlyForTrue("face").Build();
    var theThingTrueCompilerError = new BuilderFoo().BuildFooTrue()
        .WithB(5).Build(); // compiler error

    var theThingFalse = new BuilderFoo().BuildFooFalse()
        .WithB(5).Build();
    var theThingFalseCompilerError = new BuilderFoo().BuildFooFalse()
        .WithSomePropertyOnlyForTrue("face").Build(); // compiler error
}

Note the getters are only defined in the ISettings, you'd be best to make the class immutable to not allow changing after being Build(). I did not provide impls for the builders, but should be easy enough to figure out. Let me know if you do need something aside from the builder samples like at https://www.dofactory.com/net/builder-design-pattern.

Here's a quick example: https://dotnetfiddle.net/DtEidh

Kritner
  • 13,557
  • 10
  • 46
  • 72
1

No, this is not possible. If it's some kind of security question, note if you want to go crazy, you can even call internal things via reflection

The closest thing I can imagine is with interfaces:

public interface IA
{
    public bool A { get; set; }
}

public interface IB
{
    public bool B { get; set; }
}

public class Settings: IA, IB
{
    public bool A { get; set; }
    public bool B { get; set; }
}

Usage for example:

IA config = new Settings();
config.A = true; //fine
config.B = true; //error

That said, if this is a problem, your Model probably contains to much data. Maybe A and B could be seperate classes, which are properties of your model?

public class Settings
{
    public A A {get; set;}
    public B B {get; set;}
}

Or you could create a factory class

public class SettingsFactory
{
    public Settings CreateA(...)
    {
        return new Settings { ... };
    }

    public Settings CreateB(...)
    {
        return new Settings { ... };
    }
}

Anyway, you should trust your user, that he is reading your documentation.

Christian Gollhardt
  • 16,510
  • 17
  • 74
  • 111