1

I want a generic function that would work with types that have Top, Bottom, Right and Rect read-only properties - I have a lot of such classes in a third-party library.

I wrote this:

internal class MyTemplate<WhatType> {
    internal static void Work( WhatType what )
    {
        int left = what.Left;
    }
};

and I expect it to work - equivalent code in C++ would work okay. However C# objects:

error CS1061: 'WhatType' does not contain a definition for 'Left' and no extension method 'Left' accepting a first argument of type 'WhatType' could be found (are you missing a using directive or an assembly reference?)

I don't get it - why would it try to instantiate the template before I even call it? Of course, type WhatType is not yet known and so no properties can be found.

What am I doing wrong and how do I fix that?

SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
sharptooth
  • 167,383
  • 100
  • 513
  • 979

7 Answers7

8

C# generics are not templates; they are provided at runtime, not by compiler magic. As such, there is no duck-typing. Two options:

  • use a where WhatType : ISomeInterface constraint, where ISomeInterface has a Left {get;}
  • use dynamic (which provides duck-typing)

i.e.

internal class MyTemplate<WhatType> where WhatType : ISomeInterface {
    internal static void Work( WhatType what )
    {
        int left = what.Left;
    }
};
interface ISomeInterface {
    int Left { get; }
}

or:

internal class MyTemplate<WhatType> {
    internal static void Work( WhatType what )
    {
        int left = ((dynamic)what).Left;
    }
};
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

You can specify the type like this:

internal class MyTemplate<WhatType> where WhatType : LeftInterface

Then you could use the .Left call.

Where LeftInterface might look like this:

public interface LeftInterface
{   
   int Left {get; set;}
}
SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
4

C# generics look similar to C++ templates, but they're actually quite different. C# generics are strongly typed, so you can't call a member that is not statically known.

In order to be able to call Left on an object of type WhatType, you have to specify that WhatType implements an interface or inherits from a class that defines Left, using a generic constraint. For instance:

interface IHasPosition
{
    int Left { get; }
    int Top { get; }
}

internal class MyTemplate<WhatType> where WhatType : IHasPosition
{
    internal static void Work( WhatType what )
    {
        int left = what.Left;
    }
};
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
2

Generics is used for type safety. Now without any additional info about WhatType, how would the compiler know that the instance what that gets passed in will have a property Left?

You need something like this

public interface IFoo
{
    int Left {get;}
}

and

internal class MyTemplate<WhatType> where WhatType : IFoo {
Bala R
  • 107,317
  • 23
  • 199
  • 210
  • C++ copes with that easily - it just waits until the template is instantiated and then analyzes exact types. However I didn't know C# generics differ that much from C++ templates. – sharptooth Aug 05 '11 at 13:22
1

You have to specify the base class that supports .Left in the where clause or you have to cast what to a type that supports the .Left-property:

internal class MyTemplate<WhatType> where WhatType : YourBaseClassWithLeftProperty

or

internal class MyTemplate<WhatType> {
  internal static void Work( WhatType what )    {        
    YourBaseClassWithLeftProperty yourInstance=what as YourBaseClassWithLeftProperty;
    if(null != yourInstance){
         int left = yourInstance.Left;    
    }

  }
  ...
HCL
  • 36,053
  • 27
  • 163
  • 213
0

Hey boy you just need this:

Force generic interface implementation in C#

this way you can assume later on that your WhatType instance will have Left, Bottom and so on...

Community
  • 1
  • 1
Davide Piras
  • 43,984
  • 10
  • 98
  • 147
0

You haven't specified any conditions for the WhatType type, so it will accept any type, and only know what every type knows, i.e. only what's defined in the Object class.

If you want to use anything else in the type, you have to specify what it contains, which you do by specifying that it has to implement an interface or inherit from a class:

internal class MyTemplate<WhatType> where WhatType : BaseType {

Where BaseType would either be an interface or a class where the Left property is defined.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005