【问题标题】:C# : Passing a Generic ObjectC#:传递一个通用对象
【发布时间】:2012-03-12 09:56:26
【问题描述】:

我想要一个通用的打印函数...PrintGeneric(T)...在以下情况下,我缺少什么?

一如既往地感谢您的帮助/见解...

public interface ITest
{}

public class MyClass1 : ITest
{
    public string myvar = "hello 1";
}

public class MyClass2 : ITest
{
    public string myvar = "hello 2";
}

class DoSomethingClass
{

    static void Main()
    {
        MyClass1 test1 = new MyClass1();
        MyClass2 test2 = new MyClass2();

        Console.WriteLine(test1.myvar);
        Console.WriteLine(test2.myvar);             
        Console.WriteLine(test1.GetType());

        PrintGeneric(test1);
        PrintGeneric<test2.GetType()>(test2);
    }

    // following doesn't compile
    public void PrintGeneric<T>(T test)
    {
        Console.WriteLine("Generic : " + test.myvar);
    }
}

【问题讨论】:

  • var 不能是变量名,因为它是关键字。你能显示你的实际代码吗?
  • @AshBurlaczenko 不正确。 var 是 C# 3.0 中添加的上下文关键字。您可以将其用作标识符名称。同样,您可以在大多数情况下使用yieldselectfrom 等作为标识符名称。
  • 实际上“var”在不使用 Console.WriteLine 的情况下确实可以工作......但是使用关键字作为标识符名称是我的错误......
  • 在这种情况下,我们成功地猜出了您的问题所在以及错误所在。但是,您不应该过分依赖我们的精神力量 ;-) 一般来说,如果您准确地指出错误发生的代码行,并发布确切的错误消息,那么每个人的生活都会变得更轻松。
  • 哈哈抱歉,我以后会尝试这样做。谢谢!

标签: c# generics methods


【解决方案1】:

它无法编译,因为 T 可以是任何东西,而且不是所有东西都会有 myvar 字段。

您可以将myvar 设为ITest 上的属性:

public ITest
{
    string myvar{get;}
}

并将其作为属性在类上实现:

public class MyClass1 : ITest
{
    public string myvar{ get { return "hello 1"; } }
}

然后对你的方法施加一个通用的约束:

public void PrintGeneric<T>(T test) where T : ITest
{
    Console.WriteLine("Generic : " + test.myvar);
}

但在这种情况下,老实说,你最好只传递一个 ITest:

public void PrintGeneric(ITest test)
{
    Console.WriteLine("Generic : " + test.myvar);
}

【讨论】:

  • 我在 ITest 中添加了一个属性,但它告诉我需要在 MyClass1/MyClass2 中实现它...这是有道理的,但是我收到一个错误,说它已经包含 myvar 的定义
  • 哦,是的,您需要将字段更改为实现类中的属性。更新了我的答案给你看。
  • 我在 Main 中为泛型尝试了 PrintGeneric(test2) 并获得了“仅用作语句的赋值、调用、递增、递减”和 PrintGeneric(test1) 用于 PrintGeneric (ITest 测试).. 并获得“非静态字段需要对象引用”
  • 好的,我知道我需要更改为 'public static void PrintGeneric(ITest test)'...仍然不确定为什么 PrintGeneric 说只有分配/调用/增量可以用作声明
【解决方案2】:

你至少遗漏了几件事:

  • 除非你使用反射,否则类型参数需要在编译时知道,所以你不能使用

    PrintGeneric<test2.GetType()>
    

    ...虽然在这种情况下你不需要这样做

  • PrintGeneric目前对T一无所知,因此编译器找不到名为T的成员

选项:

  • ITest接口中放一个属性,将PrintGeneric改为约束T

    public void PrintGeneric<T>(T test) where T : ITest
    {
        Console.WriteLine("Generic : " + test.PropertyFromInterface);
    }
    
  • ITest 接口中放置一个属性并完全删除泛型:

    public void PrintGeneric(ITest test)
    {
        Console.WriteLine("Property : " + test.PropertyFromInterface);
    }
    
  • 如果您使用的是 C# 4,请使用动态类型而不是泛型

【讨论】:

  • 第二个代码 sn-p 中的错字,乔恩?你说删除泛型但方法上还有一个
  • @SteveHaigh:Doh,这就是我一边​​打字一边打电话的结果。已修复,谢谢。
  • 尝试第一个选项...我将“string myvar {get;}”添加到 ITest...但收到错误消息,提示 MyClass1/MyClass2 未实现接口成员“ITest.myvar”。 ..将“string myvar {get;}”添加到 MyClass1/MyClass2 会导致编译错误,说它已经定义?
  • @user1229895:你不能像那样声明一个具体的属性……但这与泛型无关。您可以使用自动实现的属性在 MyClass1 中使用public string myvar { get; private set; } 实现它。如果这还不够帮助,我建议您单独提出一个问题。
  • @Ozkan:好吧,它在这里的列表中是最后一个......虽然我想说使用动态类型而不是手动编写的反射对于有效地将反射代码卸载到一个井中是有用的——经过测试的代码库。
【解决方案3】:

您必须提供有关泛型类型T 的更多信息。在您当前的PrintGeneric 方法中,T 也可能是一个string,它没有var 成员。

您可能希望将var 更改为属性而不是字段

public interface ITest
{
    string var { get; }
}

并在PrintGeneric 方法中添加约束where T: ITest

【讨论】:

    【解决方案4】:

    在您的泛型方法中,T 只是一个类型的占位符。但是,编译器本身对运行时使用的具体类型一无所知,因此它不能假设它们会有var 成员。

    避免这种情况的常用方法是在方法声明中添加泛型类型约束,以确保使用的类型实现特定接口(在您的情况下,它可能是ITest):

    public void PrintGeneric<T>(T test) where T : ITest
    

    然后,该接口的成员将在方法中直接可用。但是,您的ITest 当前为空,您需要在其中声明通用内容以便在方法中启用其使用。

    【讨论】:

      【解决方案5】:

      试试

      public void PrintGeneric<T>(T test) where T: ITest
      {
          Console.WriteLine("Generic : " + test.@var);
      }
      

      正如@Ash Burlaczenko 所说,如果您真的希望这个带有@ 符号的前缀来转义关键字,则不能在关键字之后命名变量

      【讨论】:

      • 这行不通,ITest 没有名为 var 的成员。
      • 当然,只有在 ITest 指定属性 var 时才会起作用。我不确定它是否在示例中。
      • 我收到许多错误... CS0120:非静态字段/方法/属性需要对象引用'DoSomethingClass.PrintGeneric(T) CS0201:仅分配,调用,递增、递减、新对象可用作语句 CS0019:运算符
      【解决方案6】:

      你需要在界面中定义一些东西,比如:

      public interface ITest
      {
          string Name { get; }
      }
      

      在你的类中实现ITest

      public class MyClass1 : ITest
      {
          public string Name { get { return "Test1"; } }
      }
      
      public class MyClass2 : ITest
      {
          public string Name { get { return "Test2"; } }
      }
      

      然后将您的通用 Print 函数限制为 ITest

      public void Print<T>(T test) where T : ITest
      {
      }
      

      【讨论】:

      • 查看我对 Ash 对该问题的评论的回复。您可以使用var 作为标识符名称,就像yield 等一样,尽管这可能不是一个好主意。这是一个上下文关键字。尝试int var; 进行验证。
      • @MehrdadAfshari,啊..我没有意识到这一点。嗯,今天学到了。但是,是的,这不是我真正做过的事情:)
      【解决方案7】:

      您不能使用泛型访问var

      试试类似的东西

      Console.WriteLine("Generic : {0}", test);
      

      并覆盖ToString方法[1]

      [1]http://msdn.microsoft.com/en-us/library/system.object.tostring.aspx

      【讨论】:

        猜你喜欢
        • 2013-04-28
        • 2015-09-15
        • 2011-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-11
        相关资源
        最近更新 更多