Trivial C# class with a generic parameter wouldn't compile for no apparent reason
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?
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, whereISomeInterface
has aLeft {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;
}
};
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;
}
};
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;}
}
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 {
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;
}
}
...
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...
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.
精彩评论