1

I have 1 PDF "Window Sticker" template class that I can use for all car dealers but this one dealer wants to customize it his way, not the all dealers' way.

So, I created a 2nd PDF "Window Sticker" template class for that particular dealer.

Then I found I'm having trouble instantiating it in switch statement for any particular dealer due to scope issues. What's the workaround to it, or other way to do it?

public class Foo1
{
    public Foo1() { }
    public string GeneratePdf() { return "Red"; }
}
public class Foo2
{
    public Foo2() { }
    public string GeneratePdf() { return "Blue"; }
}

Object pdfTemplate;
long dealerAccountId = 121;  //247

switch(dealerAccountId)
{
   case 247:
       pdfTemplate = new Foo2();
       break;
   default:
       pdfTemplate = new Foo1();
       break;
}

string color = pdfTemplate.GeneratePdf();
fletchsod
  • 3,560
  • 7
  • 39
  • 65
  • 1
    Create an `IFoo` interface that has `GeneratedPdf()` in it's contract. Have both classes implement that interface. Then do `IFoo pdfTemplate;`. – ragerory Jul 07 '15 at 14:52
  • 1
    Another option is to just declare `pdfTemplate` as `dynamic`. – DavidG Jul 07 '15 at 14:54
  • @DavidG: no no no. Do not suggest that option. Just don't. See the comment chain on Patrick's answer. – siride Jul 07 '15 at 15:01
  • @siride Yes, I've already read that and I think you're being a little melodramatic about it all (though you are correct) – DavidG Jul 07 '15 at 15:01
  • 1
    @DavidG: I take it very seriously. There are some things that programmers may be tempted to do because they don't know better, but which end up creating much larger problems. I do not want this site to be a place where bad and dangerous advice is promulgated to beginner programmers. – siride Jul 07 '15 at 15:04
  • Why am I getting a -1 here? Is my question not valid? – fletchsod Jul 07 '15 at 15:04

3 Answers3

9

The problem isn't the scope. It is the type of your pdfTemplate variable. Object isn't going to cut it. You should create an interface that has the GeneratePdf method and use that as type. Else it doesn't know the possible methods of the pdfTemplate variable, like GeneratePdf.

public interface IGenerator
{
    string GeneratePdf();
}

public class Foo1 : IGenerator
{
    public Foo1() { }
    public string GeneratePdf() { return "Red"; }
}
public class Foo2 : IGenerator
{
    public Foo2() { }
    public string GeneratePdf() { return "Blue"; }
}

IGenerator pdfTemplate;
long dealerAccountId = 121;  //247

switch(dealerAccountId)
{
   case 247:
       pdfTemplate = new Foo2();
       break;
   default:
       pdfTemplate = new Foo1();
       break;
}

string color = pdfTemplate.GeneratePdf();

For the OP to understand his options with dynamic, please don't use this.

When using dynamic, it will try to resolve the method you call on run-time. The compiler doesn't warn you about a non-existing method, it will just assume it is there on run-time (in other words: bugs are only visible after compiling, and possibly after releasing the product). You don't need an interface though.

dynamic pdfTemplate;

...

string color = pdfTemplate.GeneratePdf();

This will just work, no hassle, just working code. (Check what happens if you make a typo, like generatepdf())

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Using `dynamic` would solve the problem without adding an interface (unfortunately, without type safety as well). – Sergey Kalinichenko Jul 07 '15 at 14:54
  • @dasblinkenlight I was trying to avoid that subject... :) – Patrick Hofman Jul 07 '15 at 14:54
  • 2
    @siride Because OP may not own his `Foo1` and `Foo2` classes. – Sergey Kalinichenko Jul 07 '15 at 14:55
  • That would be the only valid argument in my opinion @dasblinkenlight – Patrick Hofman Jul 07 '15 at 14:56
  • 1
    @dasblinkenlight: then OP can wrap them in classes he does own. There are all sorts of type-safe ways to solve this problem. `dynamic` exists to support interop and dynamically-typed languages, and the odd case where you have truly dynamically-constructed and managed objects. It does not exist to lazily circumvent the type system. It's very irresponsible to suggest it here, TBH. – siride Jul 07 '15 at 14:57
  • I can do with Interface and it works great now! Problem I have is not understanding what Interface is really use for and everyone have different answer. By the way, what are the example of dynamic one of you were saying so I can see what it looks like. I'm sticking to Interface. – fletchsod Jul 07 '15 at 15:03
  • @fletchsod: an interface is like a class except that it only describes what methods and properties classes that implement it may have; it does not have any implementation (working code) of its own. Read about them here: http://www.csharp-station.com/Tutorial/CSharp/lesson13. Do *not* even think about `dynamic` until you've been programming C# for at least 5 years. Just put it out of your mind. – siride Jul 07 '15 at 15:06
  • Downvoted until you remove the `dynamic` stuff. OP is not well-versed enough in C# or OOP to use `dynamic` responsibly. – siride Jul 07 '15 at 15:07
  • 2
    @siride Until (s)he's been programming in C# for 5 years, are you serious? How can you possibly make such a blanket statement? – DavidG Jul 07 '15 at 15:08
  • 4
    Indeed. Downvoting a helpful post that clearly states the *do not use* is a little harsh imho. – Patrick Hofman Jul 07 '15 at 15:08
  • @DavidG: it was a bit of a stretch, but the point is that if the OP doesn't even know about interfaces, then the OP is not going to be able to responsibly use `dynamic`. The OP should learn OOP principles and how they work in C# *first*. – siride Jul 07 '15 at 15:09
  • I have been programming C# for 14 years now. The thing is the developement team we have aren't using struct, interface, abstract, etc. that much. Sad, yep, I know. – fletchsod Jul 07 '15 at 15:09
  • 2
    @fletchsod: then this is a good time to learn about those things. And don't bother with `dynamic`. You could program the rest of your life in C# without needing to use it. – siride Jul 07 '15 at 15:11
  • @siride unless you code Office add-ins for example. I agree with you though, I barely use it. Only if I have to. – Patrick Hofman Jul 07 '15 at 15:12
  • I was just about to mention Office interop, good luck using that without dynamic :) – DavidG Jul 07 '15 at 15:13
  • @siride Wrapping would double the number of classes. `dynamic` may have been added to support interop and dynamically-typed languages, but its virtues go far beyond this narrow area of application. I also think that [static type checking tends to be overrated](http://stackoverflow.com/q/125367/335858) to a degree. Anyway, this gets too chatty, so I'm flagging the conversation for deletion by moderators. – Sergey Kalinichenko Jul 07 '15 at 15:17
  • No, just closed it instead of deleting it. I need to save this accepted answer example. – fletchsod Jul 07 '15 at 15:18
  • 2
    @fletchsod Don't worry, it's only the comments that will get deleted. The question and answers will stay. – DavidG Jul 07 '15 at 15:20
3

The problem is that pdfTemplate is of type object and type object has no method GeneratePdf(). Even though the object you assign to the variable is one of the Foo1/Foo2 types, the type of the variable itself is object. This is important.

The variable needs to be a type that has a GeneratePdf() method. It looks like both of your Foo classes have at least this method. That means they can either inherit from the same base class, or they can implement the same interface. I recommend the latter. So you'd have this:

public interface IFoo 
{
    string GeneratePdf();
}

public class Foo1 : IFoo
{
    public Foo1() { }
    public string GeneratePdf() { return "Red"; }
}
public class Foo2 : IFoo
{
    public Foo2() { }
    public string GeneratePdf() { return "Blue"; }
}

IFoo pdfTemplate;
long dealerAccountId = 121;  //247

switch(dealerAccountId)
{
   case 247:
       pdfTemplate = new Foo2();
       break;
   default:
       pdfTemplate = new Foo1();
       break;
}

string color = pdfTemplate.GeneratePdf();
siride
  • 200,666
  • 4
  • 41
  • 62
  • 2
    @musefan: I started writing this answer well before Patrick's appeared. It takes time to write an answer and apparently I am not fast enough. However, I feel like it explains the problem better, so I am leaving it up anyway. – siride Jul 07 '15 at 14:59
  • Can you add some language about why adding `dynamic` is so bad? It's taking up many of the comments which people don't read, but they may read your answer. – durron597 Jul 07 '15 at 15:22
  • @durron597: I didn't mention it at all in my answer, so I see no reason to say anything about it. – siride Jul 07 '15 at 15:24
  • I just thought it should be included in your answer so that future readers will see it without it being buried in a comment chain, that's all. – durron597 Jul 07 '15 at 15:38
-1

Try this:

public abstract class FooBase
{
    public abstract string GeneratePdf();
}

public class Foo1 : FooBase
{
    public Foo1() { }
    public override string GeneratePdf() { return "Red"; }
}

public class Foo2 : FooBase
{
    public Foo2() { }
    public override string GeneratePdf() { return "Blue"; }
}

Also, change this line:

Object pdfTemplate

Into this:

FooBase pdfTemplate;

The rest of your code is OK.

Hope this helps...

code4life
  • 15,655
  • 7
  • 50
  • 82