【问题标题】:C# Get property value without creating instance?C#在不创建实例的情况下获取属性值?
【发布时间】:2012-06-25 03:12:13
【问题描述】:

是否可以不创建实例而获得价值?

我有这门课:

public class MyClass
{
    public string Name{ get{ return "David"; } }

    public MyClass()
    {
    }
}

现在我需要获取值“David”,而不创建 MyClass 的实例。

【问题讨论】:

  • 将 Name 属性设为静态
  • 我真的很想知道这里假设的用例是什么让 OP 认为他们应该这样做。
  • @Tim:可能是 OP 试图使用非实例属性,但不知道该怎么做。类的静态属性有很多用例。
  • 当然可以。也许我应该说“假设 OP 想要的不仅仅是静态属性......”

标签: c# properties instance


【解决方案1】:

你可以使那个属性静态

public static string Name{ get{ return "David"; } } 

用法:

MyClass.Name;

【讨论】:

  • +1 因为我认为这个答案对 OP 比目前得分更高的徒劳练习更有帮助。
  • @michielvoo:好吧,您可能会争辩说我的答案的开头提供了 两个 选项 - 要么使属性静态, 要么 创建一个实例.这个答案只给出了一个:) 但是是的,我没有给出 how 使属性静态的示例,因为其他答案已经涵盖了这一点。我也相信给出答案的答案是有用的
  • 啊,这是必须接受的答案。如果我是 OP,那么我必须。
【解决方案2】:

创建静态属性:

public class MyClass
{
    public static string Name { get { return "David"; } }

    public MyClass()
    {
    }
}

像这样得到它:

string name1 = MyClass.Name;

【讨论】:

    【解决方案3】:

    创建静态类或静态属性,您不必显式实例化它。

    【讨论】:

      【解决方案4】:

      这是不可能的。由于Name 是一个实例属性,所以只有有实例才能获取它的值。

      另外,请注意,您不是在谈论参数,而是在谈论属性

      【讨论】:

        【解决方案5】:

        正如许多其他人所指出的,您可以将您的财产设为static

        public static string Name{ get{ return "David"; } }
        

        请注意,这意味着您的 MyClass 实例将不再拥有自己的 Name 属性,因为静态成员属于该类,而不是它的单个对象实例。

        编辑: 在注释中,您提到要覆盖子类中的 Name 属性。同时,您希望能够在类级别访问它(无需创建类的实例即可访问它)。

        对于静态属性,您只需在每个类中创建一个新的Name 属性。因为它们是static,所以你总是(几乎总是,耶反射)要使用特定的类来访问它们,所以你要指定你想获得哪个版本的Name。如果您想尝试破解其中的多态性并从 MyClass 的任何给定子类中获取名称,您可以使用反射来实现,但我不建议这样做。

        使用您评论中的示例:

        public class Dad 
        { 
            public static string Name { get { return "George"; }
        }
        
        public class Son : Dad
        {
            public static string Name { get{ return "Frank"; }
        }
        
        public static void Test()
        {
            Console.WriteLine(Dad.Name); // prints "George"
            Console.WriteLine(Son.Name); // prints "Frank"
            Dad actuallyASon = new Son();
            PropertyInfo nameProp = actuallyASon.GetType().GetProperty("Name");
            Console.WriteLine(nameProp.GetValue(actuallyASon, null)); // prints "Frank"
        }
        

        附带说明一下,由于您声明的属性只有一个 getter 并且它返回一个常量值,因此我建议您改用 const 或静态 readonly 变量。

        public const string Name = "David";
        public static readonly string Name = "David";
        

        两者的用法相同:

        string name = MyClass.Name;
        

        const 的主要优点(和缺点)是在编译代码时,所有对它的引用实际上都被它的值替换。这意味着它会快一点,但如果你改变它的值,你将需要重新编译所有引用它的代码。

        【讨论】:

        • 感谢大家的回复。但我需要覆盖名称属性: public class Dad { public virtual string Name { get { return "George"; } } public Dad() { } } public class Son : Dad { public override string Name { get{ return "Frank"; } } public Son() : base() { } } 可以被静态覆盖吗?
        • 静态属性不能是虚拟的,但是,您不需要它们,因为您在类级别访问它们,这意味着您始终指定您使用哪个类的 Name 属性想访问。
        • +1 表示 MyClass 的实例将不再具有 Name 属性。旁注:爸爸来自儿子?不是每个儿子都是爸爸,但每个爸爸儿子。 :P
        • 确实如此。我同意这个例子中的继承链很古怪,但我只是使用上面 cmets 中给出的例子中的方案。
        【解决方案6】:

        真正的答案:没有。它是一个 instance 属性,因此您只能在实例上调用它。您应该创建一个实例,或者将属性设为静态,如其他答案所示。

        有关静态成员和实例成员之间区别的更多信息,请参阅MSDN

        面面相觑,但仍然正确答案

        是否可以不创建实例而获得价值?

        是的,但只能通过一些非常可怕的代码创建一些 IL 传递 null 作为 this(您不会在您的财产中使用),使用 DynamicMethod。示例代码:

        // Jon Skeet explicitly disclaims any association with this horrible code.
        // THIS CODE IS FOR FUN ONLY. USING IT WILL INCUR WAILING AND GNASHING OF TEETH.
        using System;
        using System.Reflection.Emit;
        
        public class MyClass
        {
            public string Name { get{ return "David"; } }
        }
        
        
        class Test    
        {
            static void Main()
            {
                var method = typeof(MyClass).GetProperty("Name").GetGetMethod();
                var dynamicMethod = new DynamicMethod("Ugly", typeof(string), 
                                                      Type.EmptyTypes);
                var generator = dynamicMethod.GetILGenerator();
                generator.Emit(OpCodes.Ldnull);
                generator.Emit(OpCodes.Call, method);
                generator.Emit(OpCodes.Ret);
                var ugly = (Func<string>) dynamicMethod.CreateDelegate(
                               typeof(Func<string>));
                Console.WriteLine(ugly());
            }
        }
        

        请不要这样做。 曾经。太可怕了。它应该被践踏,切成小块,点燃,然后再次切割。不过很有趣,不是吗? ;)

        之所以有效,是因为它使用的是call 而不是callvirt。通常 C# 编译器会使用 callvirt 调用 即使它没有调用虚拟成员,因为它会“免费”进行空引用检查(就 IL 流而言)。像这样的非虚拟调用首先检查无效性,它只是调用成员。如果你在属性调用中检查了this,你会发现它是空的。

        编辑:正如 Chris Sinclair 所指出的,您可以使用开放的委托实例更简单地做到这一点:

        var method = typeof(MyClass).GetProperty("Name").GetGetMethod();
        var openDelegate = (Func<MyClass, string>) Delegate.CreateDelegate
            (typeof(Func<MyClass, string>), method);
        Console.WriteLine(openDelegate(null));
        

        (但同样,请不要!)

        【讨论】:

        • @R.MartinhoFernandes:绝对 :) 公平地说,我已经有一段时间没有编写任何邪恶的代码了。我将在代码中添加更多免责声明等。
        • 虽然我印象最深刻,但我几乎想否决这个答案,以防有人阅读了几乎所有内容,除了“不要这样做”部分!
        • @JonSkeet 您可以使用此处指出的技术来避免 IL 生成吗? stackoverflow.com/questions/951624/… 编辑:嗯,也许不是。这是针对方法,而不是属性。
        • 虽然没看懂但还是觉得很感动^^
        • @ChrisSinclair:不错的一个 - 我已将代码编辑为答案。
        【解决方案7】:

        您的要求确实很奇怪,但我认为您正在寻找某种元数据。您可以使用属性来实现这一点:

        public class NameAttribute : Attribute {
          public string Name { get; private set; }
          public NameAttribute(string name) {
            Name = name;
          }
        }
        
        [Name("George")]
        public class Dad { 
          public string Name { 
            get { 
              return NameGetter.For(this.GetType()); 
            }
          }
        }
        
        [Name("Frank")]
        public class Son : Dad {
        }
        
        public static class NameGetter {
          public static string For<T>() {
            return For(typeof(T));
          }
          public static string For(Type type) {
            // add error checking ...
            return ((NameAttribute)type.GetCustomAttributes(typeof(NameAttribute), false)[0]).Name;
          }
        }
        

        现在这段代码可以获取有和没有实例的名称:

        Console.WriteLine(new Dad().Name);
        Console.WriteLine(new Son().Name);
        Console.WriteLine(NameGetter.For<Dad>());
        Console.WriteLine(NameGetter.For<Son>());
        

        【讨论】:

          【解决方案8】:

          每当您编写 C# 代码时,请始终检查您的方法和属性 getter/setter 代码是否与该类的其他实例成员有任何关系。如果没有,请务必应用 static 关键字。当然这里的情况,它可以轻松解决您的问题。

          我真正发布这个问题的原因是在某些答案中存在一些语言偏见。不能对空对象调用实例方法的 C# 规则是特定的 C# 语言规则。毫无疑问,这是一个非常明智的方法,它确实有助于解决 NullReferenceExceptions,它们是在调用站点而不是在很难诊断 this 引用的方法内部的某个地方引发的空。

          但这当然不是 CLR 的要求,也不是在 CLR 上运行的每种语言的要求。事实上,即使 C# 并没有始终如一地强制执行它,您也可以在扩展方法中轻松绕过它:

          public static class Extensions {
              public static bool IsNullOrEmpty(this string obj) {
                  return obj != null && obj.Length > 0;
              }
          }
          ...
                  string s = null;
                  bool empty = s.IsNullOrEmpty();    // Fine
          

          并且使用不具有相同规则的语言的属性也可以正常工作。像 C++/CLI:

          #include "stdafx.h"
          
          using namespace System;
          using namespace ClassLibrary1;    // Add reference
          
          int main(array<System::String ^> ^args)
          {
              MyClass^ obj = nullptr;
              String^ name = obj->Name;     // Fine
              Console::WriteLine(name);
              return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-07-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-04-06
            • 1970-01-01
            • 2023-01-25
            相关资源
            最近更新 更多