-
03-07-2019 - |
質問
静的インデクサーがC#で許可されないのはなぜですか?それらが許可されるべきではない理由はわかりませんし、さらに非常に役立つ可能性があります。
例:
public static class ConfigurationManager
{
public object this[string name]
{
get => ConfigurationManager.getProperty(name);
set => ConfigurationManager.editProperty(name, value);
}
/// <summary>
/// This will write the value to the property. Will overwrite if the property is already there
/// </summary>
/// <param name="name">Name of the property</param>
/// <param name="value">Value to be wrote (calls ToString)</param>
public static void editProperty(string name, object value)
{
var ds = new DataSet();
var configFile = new FileStream("./config.xml", FileMode.OpenOrCreate);
ds.ReadXml(configFile);
if (ds.Tables["config"] == null)
ds.Tables.Add("config");
var config = ds.Tables["config"];
if (config.Rows[0] == null)
config.Rows.Add(config.NewRow());
if (config.Columns[name] == null)
config.Columns.Add(name);
config.Rows[0][name] = value.ToString();
ds.WriteXml(configFile);
configFile.Close();
}
public static void addProperty(string name, object value) =>
ConfigurationManager.editProperty(name, value);
public static object getProperty(string name)
{
var ds = new DataSet();
var configFile = new FileStream("./config.xml", FileMode.OpenOrCreate);
ds.ReadXml(configFile);
configFile.Close();
if (ds.Tables["config"] == null) return null;
var config = ds.Tables["config"];
if (config.Rows[0] == null) return null;
if (config.Columns[name] == null) return null;
return config.Rows[0][name];
}
}
上記のコードは、静的インデクサーから大きな利益を得ます。ただし、静的インデクサーが許可されていないため、コンパイルされません。これはなぜですか?
解決
インデクサー表記には、 this
への参照が必要です。静的メソッドにはクラスの特定のインスタンスへの参照がないため、 this
を使用することはできません。そのため、静的メソッドでインデクサー表記を使用することはできません。
問題の解決策は、次のようにシングルトンパターンを使用することです。
public class Utilities
{
private static ConfigurationManager _configurationManager = new ConfigurationManager();
public static ConfigurationManager ConfigurationManager => _configurationManager;
}
public class ConfigurationManager
{
public object this[string value]
{
get => new object();
set => // set something
}
}
インデクサー表記を使用して Utilities.ConfigurationManager [&quot; someKey&quot;]
を呼び出すことができます。
他のヒント
それはひどく有用ではないと考えられていたと思います。私もそれは残念だと思います-私が使用する傾向がある例はEncodingです。 非常に頻繁に出てくるとは思いませんが、他のこととは別に、利用できないのは少し矛盾しているように感じます。
チェックする必要がありますが、IL(中級言語)で既に利用可能です疑い。
回避策として、シングルトン/静的オブジェクトにインスタンスインデクサーを定義できます(たとえば、ConfigurationManagerは静的クラスではなくシングルトンです):
class ConfigurationManager
{
//private constructor
ConfigurationManager() {}
//singleton instance
public static ConfigurationManager singleton;
//indexer
object this[string name] { ... etc ... }
}
C#6の新しいコンストラクトでは、プロパティ式の本体を使用してシングルトンパターンを簡素化できます。 たとえば、次のショートカットを使用して、コードレンズで適切に動作するようにしました。
public static class Config
{
public static NameValueCollection Get => ConfigurationManager.AppSettings;
}
これは、古いコードをアップグレードし、アプリケーション設定へのアクセスを統一するために、find-replace-ableであるという追加の利点があります。
また、属性を保存するための静的インデクサーが(必要に応じて)必要であったため、やや厄介な回避策を見つけました:
静的インデクサー(ここではElement)を使用するクラス内で、同じ名前+&Dict&quot;のサブクラスを作成します。そのサブクラスのインスタンスとして読み取り専用の静的を指定し、目的のインデクサーを追加します。
最後に、クラスを静的インポートとして追加します(したがって、サブクラスは静的フィールドのみを公開します)。
import static Element.ElementDict;
public class Element {
// ....
private static readonly Dictionary<string, object> elemDict = new Dictionary<string, object>();
public class ElementDict {
public readonly static ElementDict element = new ElementDict();
public object this[string key] {
get => elemDict.TryGetValue(key, out object o) ? o : null;
set => elemDict[key] = value;
}
}
}
そしてタイプとして大文字で使用するか、辞書として使用せずに使用できます:
var cnt = element["counter"] as int;
element["counter"] = cnt;
しかし、残念ながら、実際に&quot; value&quot; -Typeとしてオブジェクトを使用する場合、以下は(少なくとも宣言として)さらに短くなり、即時型キャストも提供します:
public static T load<T>(string key) => elemDict.TryGetValue(key, out object o) ? (T) o : default(T);
public static void store<T>(string key, T value) => elemDict[key] = value;
var cnt = Element.load<int>("counter");
Element.store("counter", cnt);
thisキーワードは、クラスの現在のインスタンスを参照します。静的メンバー関数にはthisポインターがありません。 thisキーワードを使用して、コンストラクター、インスタンスメソッド、およびインスタンスアクセサー内からメンバーにアクセスできます。( msdn )。これはクラスのインスタンスを参照するため、静的はクラスのインスタンスに関連付けられていないため、静的の性質と競合します。
1つの回避策は、プライベートに対してインデクサーを使用できるようにする次の方法です。 辞書。新しいインスタンスを作成するだけで、静的部分にアクセスできます。
public class ConfigurationManager
{
public ConfigurationManager()
{
// TODO: Complete member initialization
}
public object this[string keyName]
{
get
{
return ConfigurationManagerItems[keyName];
}
set
{
ConfigurationManagerItems[keyName] = value;
}
}
private static Dictionary<string, object> ConfigurationManagerItems = new Dictionary<string, object>();
}
これにより、クラスのメンバーへのアクセス全体をスキップし、インスタンスを作成してインデックスを作成することができます。
new ConfigurationManager()["ItemName"]
理由は、静的インデクサーを使用して正確にインデックスを作成する内容を理解することが非常に難しいためです。
コードは静的インデクサーの恩恵を受けると言いますが、本当にそうでしょうか?これを変更するだけです:
ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)
これに:
ConfigurationManager[name] = value
...
value = ConfigurationManager[name]
これにより、コードが改善されることはありません。それはコードの多くの行によって小さくなりません、オートコンプリートのおかげで書くのは簡単ではなく、「プロパティ」と呼ぶものを取得して設定しているという事実を隠し、実際に読者に強制しますインデクサーが返すまたは設定する内容に関するドキュメントを読んでください。これは、インデックスを作成するプロパティであることは明らかではないためです。 <*>
大声で読み上げて、コードの機能をすぐに理解できます。
書くのが速いコードではなく、理解しやすい(=速い)コードを書きたいことを思い出してください。コードを配置できる速度とプロジェクトを完了する速度を間違えないでください。