获取类中声明的变量名



我想做这样的事情——在对象中捕获原始声明对象的变量名。

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"
}    
}

可以理解,上面的方法不起作用,而且我知道变量名没有存储在运行时元数据中,所以这个问题的一般答案是无法做到这一点。

也就是说,研究让我找到了几种变通方法,我正在寻找最好的方法。

这个问题很好地解决了建议:

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

然后:

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

我还没有测试以上是否有效,但缺点对我来说是有问题的。

我已经找到了——但很难找到一个更早的答案,我发现这个问题的名字在编译时被替换了。

如果有人能解决这个问题(即使速度慢得可怕(,或者知道编译时替换的方法,我会非常感兴趣。

如果我可以停止方法内部的实例化,上面提出的解决方法将对我有效。

编辑对于上下文,这里有一个我使用普通枚举的例子——我宁愿用我自己的强类型类型替换枚举: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);
}
}

查看您上次添加的内容,我不建议使用枚举类型来确定数据库。如果您的枚举类型在稍后阶段删除了值,则会出现问题,例如

enum ConfiguredDatabase
{
Database1,
// Database2, 
Database3
}

现在Database3的值与Database2过去的值相同。如果为每个分配固定值,那么在调用的代码中仍然可以使用分配给Database2的值!

相反,我建议在这里坚持一些依赖注入原则,将接口传递给一个具体的类类型。

例如,沿着这些路线的东西。

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

然后在其他地方你称之为:

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

我知道我的例子并不完全符合您的代码,但我希望它能让您了解我的建议。

如果您真的需要使用反射来确定您的调用属性是什么,那么我建议您调整以下模式。我在MVVM for ViewModel类中使用了它,因此使用了OnPropertyChanging和OnPropertyChangingEvents,但我相信您会明白的。

您可以调整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);
}   
}

最新更新