简体   繁体   English

获取类中声明的变量名

[英]Get the declared variable name in a class

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.编辑上下文这里是我使用普通枚举的示例 - 我宁愿用我自己的强类型类型替换枚举:ConfiguredDatabase 是一个枚举。

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);
            }
        }

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 eg如果您的枚举类型在稍后阶段删除了值,则会出现问题,例如

enum ConfiguredDatabase
{
  Database1,
  // Database2, 
  Database3
}

Now Database3 has the same value as Database2 had in the past.现在 Database3 的值与过去 Database2 的值相同。 If you assign fixed values for each then it will still be possible to use the value assigned to Database2 in your called code!如果您为每个分配固定值,那么仍然可以在您的调用代码中使用分配给 Database2 的值!

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.我知道我的示例并不完全适合您的代码,但我希望它能让您了解我的建议。

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.我在 MVVM 中将它用于 ViewModel 类,因此使用 OnPropertyChanging 和 OnPropertyChanging 事件,但我相信您会明白这个想法。

You may be able to adapt BaseClass.SetProperty to call other code or raise events to suit your needs.您可以调整 BaseClass.SetProperty 以调用其他代码或引发事件以满足您的需要。

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);
    }   
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM