1

I want to do something like this - where I capture the original declaring objects variable name inside the object.

 public class Foo
    {
        private string _originalDeclarer;
        
        public Foo(string originalDeclarer=nameof(this))
        {
            _originalDeclarer = originalDeclarer;
        }

        public string OriginalDeclarer
        {
            get => _originalDeclarer;
            set => _originalDeclarer = value;
        }
    }

    public static class Bar
    {
        public static void CreateFoos()
        {
            Foo GreenFoo = new Foo();
            Foo BlueFoo = new Foo();
            
            Console.WriteLine(GreenFoo);
            Console.WriteLine(BlueFoo);
            
            //Expected output
            // "GreenFoo"
            // "BlueFoo"
        }    
    }

The above understandably doesn't work, and I understand that variable names are not stored in runtime metadata, so the general answer to this question is that it cannot be done.

That said, research leads me to several workarounds, and I am looking for the best one.

This question does a good job with the proposal:

class Self
{
    public string Name { get; }

    public Self([CallerMemberName] string name = null)
    {
        this.Name = name;
    }
}

Then:

class Foo
{
    private Self me = new Self(); // Equivalent to new Self("me")

    public void SomeMethod()
    {
        // Can't use the default here, as it would be "SomeMethod".
        // But we can use nameof...
        var joe = new Self(nameof(joe));
    }
}

I've yet to test the above if it works, but the drawback would be problematic for me.

I have - but struggling to find an earlier answer I found to this question where the names where substituted at compile time.

If anyone has ways around this problem (even if it horrifically slow) or knows how the compile-time substitution I would be very interested.

The above propose workaround would work for me if I could stop instantiation inside a method.

Edit For context here is an example where I use normal Enums - I would rather replace an enum with my own strongly typed type: ConfiguredDatabase is an enum.

private Result<DatabaseConnectionStatus> TestDatabase(ConfiguredDatabase database)
        {
            SqlDataAccessLayer sqlDataAccessLayer = DetailsStore.DataAccessLayers.TryGetbyUId(database.ToString());

            if (sqlDataAccessLayer.ConnectionDetails.DataSource == string.Empty)
            {
                return Result.Failed($"Database connection is not configured for {database}", DatabaseConnectionStatus.NoConnectionConfigured);
            }
        }
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 3
    That is a reference types. Multiple variables can point to a single instance. Or no named variable point to such an instance. Think of a List. Somehow letting the instances now of all the variables names pointing to it doesn't look helpful to me. It could only work in an absolutely subset of cases that would need to be enforced by a certain programming style that would take the language of most of its power. – Ralf Oct 23 '20 at 07:48
  • 3
    `var bob = new Foo(); var flub = bob;` your world just broke, you need to rethink your problem – TheGeneral Oct 23 '20 at 07:49
  • Lets take a step back, why do you need to do this? What program are you writing that the name of the variable makes a difference? – TheGeneral Oct 23 '20 at 08:03
  • @TheGeneral yes, I should have cited in my question that I was aware of this downside. Taking a step back. I want to write a custom Enum class. Given a situation where you want to strongly type something, rather than pass in string names, you can use enums for your alias. Although there is a number of limitations to Enums which I want to extend. For example easy traversing the list of enums, and implicit conversion to strings. This article discusses it nicely - https://stackoverflow.com/questions/261663/can-we-define-implicit-conversions-of-enums-in-c – Gregory William Bryant Oct 23 '20 at 08:24
  • @TheGeneral Here is an example where I am currently using normal Enums; – Gregory William Bryant Oct 23 '20 at 08:29

2 Answers2

0

Looking at your last addition, I wouldn't recommend using an enumerated type to determine your database. There are issues if your enumerated type has a value removed at a later stage e.g.

enum ConfiguredDatabase
{
  Database1,
  // Database2, 
  Database3
}

Now Database3 has the same value as Database2 had in the past. If you assign fixed values for each then it will still be possible to use the value assigned to Database2 in your called code!

Instead I'd recommend sticking to some dependency injection principles here by passing the interface to a concrete class type.

Something along these lines, for example.

public interface IConfiguredDatabase
{
  string ConnectionString;
}

public Database1 : IConfiguredDatabase
{
  public Database1
  {
    ConnectionString = "Database One";
  }
  public string ConnectionString{get;set;}
}

public Database2 : IConfiguredDatabase
{
  public Database1
  {
    ConnectionString = "Database Two";
  }
  public string ConnectionString{get;set;}
}

private Result<DatabaseConnectionStatus> TestDatabase(IConfiguredDatabase database)
        {
            SqlDataAccessLayer sqlDataAccessLayer = DetailsStore.DataAccessLayers.TryGetbyUId(database.ConnectionString);

            if (sqlDataAccessLayer.ConnectionDetails.DataSource == string.Empty)
            {
                return Result.Failed($"Database connection is not configured for {database.ConnectionString}", DatabaseConnectionStatus.NoConnectionConfigured);
            }
        }

then somewhere else you call it as:

using (var d = new Database1()
{
  var result = TestDatabase(d);

...
}

I know that my example doesn't fit your code exactly, but I hope that it gives you an idea of what I'm suggesting.

ChrisBD
  • 9,104
  • 3
  • 22
  • 35
  • in practice what I have (in other code) is not far off this. The element I'm trying to wrap is 'naming' these databases. I.e. Database1 is called "Database1", and that name can be retrieved, parsed and strongly typed. At the moment I'm faced with storing them in a dictionary, which only accepts keys that match enumerations of my type or walking through a strongly typed object with reflection. I was hoping to move somewhere with little more flexibility. – Gregory William Bryant Oct 23 '20 at 12:01
  • The problem extends to anything where you're using yourdictionary["key"].value. I want to have strongly typed keys, so the consumer always knows what keys are available at compile time. Also allowing for compile-time errors. – Gregory William Bryant Oct 23 '20 at 12:02
0

If you really need to use reflection to determine what your calling property was then I would suggest that you adapt the following pattern. I use it in MVVM for ViewModel classes, hence the use of OnPropertyChanging and OnPropertyChanging Events, but I'm sure that you'll get the idea.

You may be able to adapt BaseClass.SetProperty to call other code or raise events to suit your needs.

public class BaseClass
{
    /// <summary>
    ///     Worker function used to set local fields and trigger an OnPropertyChanged event
    /// </summary>
    /// <typeparam name="T">Parameter class</typeparam>
    /// <param name="backingStore">Backing field referred to</param>
    /// <param name="value">New value</param>
    /// <param name="propertyName">Property that this value is applied to </param>
    /// <param name="onChanged">Event handler to invoke on value change</param>
    /// <param name="onChanging">Event handler to invoke on value changing</param>
    /// <returns></returns>
    protected bool SetProperty<T>(
        ref T backingStore, T value,
        [CallerMemberName] string propertyName = "",
        Action onChanged = null,
        Action<T> onChanging = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value)) return false;

        onChanging?.Invoke(value);
        OnPropertyChanging(propertyName);

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }

    /// <summary>
    ///     INotifyPropertyChanging event handler
    /// </summary>
    public event PropertyChangingEventHandler PropertyChanging;

    /// <summary>
    ///     INotifyOnPropertyChanging implementation
    /// </summary>
    /// <param name="propertyName">Class property that is changing</param>
    protected void OnPropertyChanging([CallerMemberName] string propertyName = "")
    {
        var changing = PropertyChanging;

        changing?.Invoke(this, new PropertyChangingEventArgs(propertyName));
    }

    /// <summary>
    ///     INotifyPropertyChanged event handler
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    ///     INotifyPropertyChanged implementation
    /// </summary>
    /// <param name="propertyName">Class property that has changed</param>
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        var changed = PropertyChanged;

        changed?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

public class SomeClass : BaseClass
{
    private int _propertyOne;
    private string _propertyTwo;
    
    public int PropertyOne
    {
        get=> return _propertyOne;
        set=> SetProperty(ref _propertyOne, value);
    }
    
    public int PropertyTwo
    {
        get=> return _propertyOne;
        set=> SetProperty(ref _propertyTwo, value);
    }   
}
ChrisBD
  • 9,104
  • 3
  • 22
  • 35
  • This is very similar to the implementation I have for objects and collections, but it not what I'm looking for in this case. I'm trying to type my objects and have them simple to create and strongly typed. I.e. ColourSettings = new Setting(). Its gets the variable name and knows that it's colour setting. Rather than having to type ColourSettings = new Setting(name of(ColourSettings); – Gregory William Bryant Oct 23 '20 at 12:04