views:

119

answers:

3

I have a class that needs to run in both Silverlight and non-Silverlight runtimes. However, the behavior is slightly different so I need something like...

if(isRunningInSilverlight) {
  // do this
} else {
  // do that
}

How do I correctly assign isRunningInSilverlight?

+3  A: 

Considering that different compilers are used to create Silverlight and non-Silverlight assemblies, you could use compiler directives and conditionally compile your code rather than detect the difference at runtime. Just define SILVERLIGHT (or some other define) for the Silverlight build and then have:

#if SILVERLIGHT
// Do silverlight stuff
#else
// Do other stuff
#endif

You could use the ConditionalAttribute as well with this approach.

Jeff Yates
+4  A: 

Jeff's solution is direct and will work. However, if you are adverse to #if compiler directives popping up all over your code, you can centralize and abstract it all away with a little interface magic.

Consider the following,

// a nice neat public interface to centralize all of your 
// run time requirements
public interface IRuntimeInfo
{
    // true if silverlight runtime, false if full-Clr
    bool IsSilverlight { get; }
}

with an implementation like

public class RuntimeInfo : IRuntimeInfo
{
    public bool IsSilverlight { get; private set; }
    public RuntimeInfo ()
    {
        // @Jeff's compiler directives - er, taking his
        // word 'SILVERLIGHT' is actually defined
        #if SILVERLIGHT
            IsSilverlight = true;
        #else
            IsSilverlight = false;
        #endif
    }
}

in your consumers

public class MyClass
{
    private readonly IRuntimeInfo _runtimeInfo = null;
    public MyClass (IRuntimeInfo runtimeInfo)
    {
        _runtimeInfo = runtimeInfo;
    }
    public void SomeMethod ()
    {
        if (_runtimeInfo.IsSilverlight)
        {
            // do your thang
        }
        else
        {
            // do some other thang
        }
    }
}

and now you can test independent of the actual runtime

// testing silverlight behaviour of MyClass under full CLR
[TestMethod]
public void Test_SilverlightBehaviour ()
{
    // setup mock, using Moq below
    Mock<IRuntimeInfo> _mockRuntimeInfo = new Mock<IRuntimeInfo> ();
    _mockRuntimeInfo.Setup (r => r.IsSilverlight).Returns (true);
    // pass mock to consumer
    MyClass myClass = new MyClass (_mockRuntimeInfo);
    // test silverlight-specific behaviour
    myClass.SomeMethod ();
}

// testing CLR behaviour of MyClass under full CLR
[TestMethod]
public void Test_FullClrBehaviour ()
{
    // setup mock, using Moq below
    Mock<IRuntimeInfo> _mockRuntimeInfo = new Mock<IRuntimeInfo> ();
    _mockRuntimeInfo.Setup (r => r.IsSilverlight).Returns (false);
    // pass mock to consumer
    MyClass myClass = new MyClass (_mockRuntimeInfo);
    // test full-Clr-specific behaviour
    myClass.SomeMethod ();
}

and now in prod, you can use a container, factory, or default constructor of your choice, to ensure concrete implementation is passed. For example, revisiting MyClass snippet from above,

public class MyClass
{
    // default constructor. personally, I would opt to use a container
    // like Castle Windsor Container, or Microsoft's Unity, but if you
    // have no control over application or it's just this "one" thing,
    // just default to a concrete implementation below.
    public MyClass () : this (new RuntimeInfo ()) { }
    // what we call an "injector" constructor, because runtime info is
    // passed - or "injected" - into instance.
    public MyClass (IRuntimeInfo runtimeInfo) { ... }
    ...
}
johnny g
+1 for an extremely good answer.
TheMissingLINQ
It's nice but it should be noted that while this hides #if stuff (why you'd want to, I don't know), this will result in larger assemblies for Silverlight deployment as they will contain both versions of the code. Also, it means you can only use types that exist in both uses.
Jeff Yates
it's a trade off. i shy away from compiler directives because they are difficult to test [you would require an exhaustive cross product of all directives to accurately test application behaviour]. considerations such as assembly bloat, Type availability, must also be made - but that is up to the user. this is just *one* possible solution :)
johnny g
You don't need an exhaustive cross product. You only test the configurations that are valid builds. There's no point in testing anything else.
Jeff Yates
A: 

The suggested solutions that use pre-processor directives will certainly work, but it seems like a simpler solution would be to just write a base class and then derive both a Silverlight and a non-Silverlight version from it. I would think it would be much easier to maintain and test that way, not to mention enhancing readability of your code overall.

Seeing if statements based on runtime environments strikes me like all of the worst stuff about programming with Javascript and I hate having code that isn't relevent to my project mucking about where I don't need to see it. On that path lies danger.

Raumornie