C# : Passing a Generic Object

122,685

Solution 1

It doesn't compile because T could be anything, and not everything will have the myvar field.

You could make myvar a property on ITest:

public ITest
{
    string myvar{get;}
}

and implement it on the classes as a property:

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

and then put a generic constraint on your method:

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

but in that case to be honest you are better off just passing in an ITest:

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

Solution 2

You're missing at least a couple of things:

  • Unless you're using reflection, the type arguments need to be known at compile-time, so you can't use

    PrintGeneric<test2.GetType()>
    

    ... although in this case you don't need to anyway

  • PrintGeneric doesn't know anything about T at the moment, so the compiler can't find a member called T

Options:

  • Put a property in the ITest interface, and change PrintGeneric to constrain T:

    public void PrintGeneric<T>(T test) where T : ITest
    {
        Console.WriteLine("Generic : " + test.PropertyFromInterface);
    }
    
  • Put a property in the ITest interface and remove the generics entirely:

    public void PrintGeneric(ITest test)
    {
        Console.WriteLine("Property : " + test.PropertyFromInterface);
    }
    
  • Use dynamic typing instead of generics if you're using C# 4

Solution 3

You'll have to provide more information about the generic type T. In your current PrintGeneric method, T might as well be a string, which does not have a var member.

You may want to change var to a property rather than a field

public interface ITest
{
    string var { get; }
}

And add a constraint where T: ITest to the PrintGeneric method.

Solution 4

In your generic method, T is just a placeholder for a type. However, the compiler doesn't per se know anything about the concrete type(s) being used runtime, so it can't assume that they will have a var member.

The usual way to circumvent this is to add a generic type constraint to your method declaration to ensure that the types used implement a specific interface (in your case, it could be ITest):

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

Then, the members of that interface would be directly available inside the method. However, your ITest is currently empty, you need to declare common stuff there in order to enable its usage within the method.

Solution 5

try

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

as @Ash Burlaczenko has said you cant name a variable after a keyword, if you reallllly want this prefix with @ symbol to escape the keyword

Share:
122,685

Related videos on Youtube

user1229895
Author by

user1229895

Updated on April 01, 2020

Comments

  • user1229895
    user1229895 about 4 years

    I want to have a generic print function...PrintGeneric(T)...in the following case, what am I missing?

    As always your help/insight is appreciated...

    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);
        }
    }
    
    • Ash Burlaczenko
      Ash Burlaczenko about 12 years
      var cannot be a variable name as it is a keyword. Can you show your actual code.
    • mmx
      mmx about 12 years
      @AshBurlaczenko Not correct. var is a contextual keyword added in C# 3.0. You can use it as an identifier name. Similarly, you can use yield, select, from, etc. as identifier names in most contexts.
    • user1229895
      user1229895 about 12 years
      actually "var" did work without using just Console.WriteLine...but that was my bad to use a keyword as a identifier name...
    • Péter Török
      Péter Török about 12 years
      In this case we managed to guess successfully where your problem lied and what the error was. However, you shouldn't rely on our psychic powers too much ;-) In general, it makes everyone's life easier if you precisely indicate the code line where the error occurs, and post the exact error message too.
    • user1229895
      user1229895 about 12 years
      haha sorry I'll try to do so in the future. thx!
  • mmx
    mmx about 12 years
    This will not work, ITest does not have a member named var.
  • Alexander R
    Alexander R about 12 years
    Only going to work if ITest specifies the property var, of course. I'm not sure it does in the example.
  • user1229895
    user1229895 about 12 years
    i get a number of errors... CS0120 : An Object Reference is Required for non-static field/method/property 'DoSomethingClass.PrintGeneric<T>(T) CS0201 : Only Assignment, call, increment, decrement, new object can be used as a statement CS0019 : operator < cannote be applied to operands of type Method Group CS1061 : T does not contain definition myvar
  • mmx
    mmx about 12 years
    See my reply on Ash's comment to the question. You can use var as identifier name, just like yield, etc., though that's probably not a great idea. It's a contextual keyword. Try int var; to verify for yourself.
  • Moo-Juice
    Moo-Juice about 12 years
    @MehrdadAfshari, ah.. I was not aware of that. Well, today I learned. But yes, it's not something I'd ever actually do :)
  • Steve
    Steve about 12 years
    Typo in your second code snippet there Jon? You said remove generics but there's still a <T> on the method?
  • user1229895
    user1229895 about 12 years
    I added a property to ITest, but then it tells me I need to implement it in MyClass1/MyClass2...which makes sense, but then I get an error saying it already contains a definition for myvar
  • Jon Skeet
    Jon Skeet about 12 years
    @SteveHaigh: Doh, that's what I get for typing and talking on the phone at the same time. Fixed, thanks.
  • jonnystoten
    jonnystoten about 12 years
    Oh yeah, you need to then change the fields to be properties in the implementing classes. Updated my answer to show you.
  • user1229895
    user1229895 about 12 years
    trying the first option...I add "string myvar {get;}" to ITest... but get an error saying MyClass1/MyClass2 don't implement Interface member 'ITest.myvar'...adding "string myvar {get;}" to MyClass1/MyClass2 results in compile error saying its already defined?
  • Jon Skeet
    Jon Skeet about 12 years
    @user1229895: You can't declare a concrete property like that... but that has nothing to do with generics. You can use an automatically implemented property to implement it in MyClass1 with public string myvar { get; private set; }. If that's not enough help, I suggest you ask a separate question.
  • user1229895
    user1229895 about 12 years
    i tried PrintGeneric<test2.GetType()>(test2) in Main for the generic and get an 'Only assignment, call, increment, decrement used as a statement' and PrintGeneric(test1) for PrintGeneric(ITest test)..and get a "An object reference is required for non-static field"
  • user1229895
    user1229895 about 12 years
    ok, i see I see I need to change to 'public static void PrintGeneric(ITest test)'...still unsure why PrintGeneric<T> says only assignment/call/increment can be used as a statement
  • Ozkan
    Ozkan almost 4 years
    As far as I know dynamic types are not recommended for basic functionalities like this. It should be your last resort.
  • Jon Skeet
    Jon Skeet almost 4 years
    @Ozkan: Well it is last in the list here... although I'd say that using dynamic typing instead of manually-written reflection can be useful to effectively offload the reflection code to a well-tested codebase.