Unoffical empeg BBS

Quick Links: Empeg FAQ | RioCar.Org | Hijack | BigDisk Builder | jEmplode | emphatic
Repairs: Repairs

Topic Options
#366228 - 10/03/2016 03:25 C# question - type <T> as a variable
tfabris
carpal tunnel

Registered: 20/12/1999
Posts: 31600
Loc: Seattle, WA
I have some code I'm working on that is stumping me. I'm having trouble putting the right words into Google to find the correct answer.

I need to test some code (which I cannot change) by calling the code's high-level entry point here:
Code:
        public static T FindOne<T>(string propertyName, object propertyValue) where T : Entity
        {
            return (T)Entity.FindOne(typeof(T), propertyName, propertyValue);
        }



In this case, "T" can be a fairly large variety of sub-classes of the "Entity" type. Each sub class is interesting in its own way and has all sorts of different kinds of members to the class. I already wrote some separate independent tests which each called that entry point separately. Like this:

Test 1:
Code:
var objectToTest = Entity.FindOne<TheFirstOfThoseInterestingSubClasses> ("Id", this.testData.Id.ToString());

Test 2:
Code:
var objectToTest = Entity.FindOne<TheSecondOfThoseInterestingSubClasses> ("Id", this.testData.Id.ToString());

Test 3:
Code:
var objectToTest = Entity.FindOne<TheThirdOfThoseInterestingSubClasses> ("Id", this.testData.Id.ToString());

( . . . ) (etc)
Each time, "objectToTest" is returned as another one of those interesting sub-classes, which I can then test and check.

My code reviewer rightfully suggested that, instead, my test should be refactored as a single parent test class with child classes that invoked the tests on the various objects. This would mean less code duplication and higher maintainability.

However, in this new refactored arrangement, I can't put the name of the interesting sub class directly into the angle brackets, since, I don't know what it's going to be each time round. It has to be determined at run time by querying my test input object. What I want to do is something like this:

Code:
var objectToTest = Entity.FindOne<MyTestInputObject.GetType()> ("Id", this.testData.Id.ToString());


Which doesn't work, and results in the cryptic error "Operator '<' cannot be applied to operands of type 'method group' and 'Type'". The error, of course, has nothing to do with what's really wrong, which is, I can't put a variable inside those brackets since the type has to be determined at compile time rather than runtime.

What is my workaround here? I've tried googling, but I can't seem to get the search terms correct. I keep coming up with hits that almost get close to what I want to do, but not quite, not enough to work the way I need them to work. I've actually tried some of them and none of the examples have anything that looks the same as what I'm trying to do. For example there's one with a Method.Invoke that looks promising, but I can't get any of the parameters correct to make it work.

Anyone happen to know this one?
_________________________
Tony Fabris

Top
#366230 - 10/03/2016 03:37 Re: C# question - type <T> as a variable [Re: tfabris]
tfabris
carpal tunnel

Registered: 20/12/1999
Posts: 31600
Loc: Seattle, WA
Okay, I think I got it. This is a massive pain, but I think it works.

Code:
            Type testedType = MyTestInputObject.GetType();
            MethodInfo mi = typeof(Entity).GetMethod("FindOne", new Type[] { typeof(string), typeof(string) });
            MethodInfo fooRef = mi.MakeGenericMethod(testedType);
            var objectToTest = fooRef.Invoke(null, new object[] { "Id", this.testData.Id.ToString() });


Edited by tfabris (10/03/2016 04:08)
_________________________
Tony Fabris

Top
#366232 - 10/03/2016 08:21 Re: C# question - type <T> as a variable [Re: tfabris]
Roger
carpal tunnel

Registered: 18/01/2000
Posts: 5683
Loc: London, UK
Why don't you just test the 3-parameter version of the function? You don't have to test everything. The point of testing is to give you confidence that the code works, not to achieve 100% nirvana.

Code:
var x = Entity.FindOne(MyTestInputObject.GetType(), "Id", testData.Id.ToString());
_________________________
-- roger

Top
#366233 - 10/03/2016 11:17 Re: C# question - type <T> as a variable [Re: tfabris]
DWallach
carpal tunnel

Registered: 30/04/2000
Posts: 3810
Not that this is particularly helpful to you, but you're doing something that's relatively straightforward in C# that's damn near impossible in Java because Java doesn't have "reified generics", i.e., you can't say "typeof(T)" because T isn't there at all at runtime.

Java programmers instead pass around "class" objects and play awful games with Java reflection.

So... be happy you can do this at all, I guess...

Top
#366234 - 10/03/2016 18:15 Re: C# question - type <T> as a variable [Re: tfabris]
tfabris
carpal tunnel

Registered: 20/12/1999
Posts: 31600
Loc: Seattle, WA
Roger: Good point. Though if I'd done that, I wouldn't have learned the crazy work-around. (Which I can now use in other places where I might not have that choice.)

Dan: Interesting. I always got the feeling that C# came from a place of Microsoft-is-jealous-of-Java place. They seem to be trying to one-up Java. smile
_________________________
Tony Fabris

Top
#366237 - 10/03/2016 20:58 Re: C# question - type <T> as a variable [Re: tfabris]
DWallach
carpal tunnel

Registered: 30/04/2000
Posts: 3810
Roger's workaround, as best I understand it, is asking a concrete object for its type. That's more or less what you have to do in Java, so there's that.

Lately, I've been playing a lot with Kotlin, which is to Java what Swift is to Objective-C -- a new layer of shiny goodness built on a shambling pile of old weird yuck. Kotlin is brought to you by the IntelliJ guys, who use it for building IntelliJ, so they're highly motivated to make it work and work well.

That said, Kotlin still carries warts from its Java heritage, much like Swift does from Objective-C. Notably, Kotlin only has reified generic types under very limited circumstances, namely only on declared inline functions, so they avoid the specialization confusion that C# has to throw compiler optimizations at. I haven't yet needed reified generics yet in my own hacking, so I don't have a good sense of whether Kotlin does it "good enough" or not.

One last factoid: In Java, when they added generics, they made the Class class be parametric on its own type. Which is weird, but it means that you can declare a method kinda like:

public static <T> T foo(Class<T> clazz, ...) {
}

That would be a function, parameterized on T, which returns a T, and takes a Class object that must be of the same type, which can then be enforced at compile time, and you subsequently know that it's safe to say "clazz.newInstance()" which will give you a T.

So I give them partial credit for small levels of elegance, but it's awfully nice when you never need to see a Class class.

Top
#366241 - 11/03/2016 11:39 Re: C# question - type <T> as a variable [Re: tfabris]
Tim
veteran

Registered: 25/04/2000
Posts: 1529
Loc: Arizona
Originally Posted By: tfabris
Dan: Interesting. I always got the feeling that C# came from a place of Microsoft-is-jealous-of-Java place. They seem to be trying to one-up Java. smile

One of the advantages of not being first to the market is the ability to improve on the competitor's offering. We see that a lot in aerospace (and automobiles, etc).

Top