A true cross-platform .NET library (part 2)

A true cross-platform .NET library (part 2)

In part 1 of this series I showed that PCLs may not be a “one size fits all” solution to target all .NET subsets. You may need to use shared projects and file links. To maximize code reuse in these other projects, we end up having to manage code that is specific to .NET subsets. Most people use preprocessor directives, a method inherited from C/C++, but the result may be something like this:

namespace NetFabric
{
#if NET35 || UNITY
[Serializable]
#endif
  public struct Angle
#if !MF_FRAMEWORK
    : IEquatable<Angle>
    , IComparable
    , IComparable<Angle>
    , IFormattable
#endif
  {
#if UNITY
  [SerializeField]
#endif
  double radians;
  }
}

Many people finds this messy and confusing.

Partial classes and structs

C# lets you split the declaration of a class, struct, interface or method into multiple source files. The compiler merges and compiles these as if they’ve always been one single file. This makes it possible to split the platform specific code into separate files. We just have to add to each project only the files containing the portions that are relevant to it.

The NetFabric.Angle project uses this method and splits the Angle struct definition into the following files:

  • Angle.cs – containing the parts common to all platforms.
  • Angle.Net.cs – containing the parts common to .NET 3.5 and the PCL projects.
  • Angle.Net35.cs – containing the parts specific to .NET 3.5.
  • Angle.NetMF.cs – containing the parts specific to .NET Micro Framework.
  • Angle.Unity.cs – containing the parts specific to Unity.

Check out these files and notice that all files declare a public partial struct Angle but each one has its own set of attributes and interface implementations.

 

Partial methods

It’s possible that the same method may have different signatures on different platforms. For example, the constructor for ArgumentOutOfRangeException doesn’t have the paramValue argument when on .NET Micro Framework.

Using the partial keyword on a method makes it possible to have a method with different implementations on different platforms. This can be used to manage the different signatures of a method.

First, declare in the class or structure that will make the call, the signature of a new method that contains all the arguments, and replace the previous method call by a call to this new method:

 static partial void ThrowArgumentOutOfRange(string paramName, object paramValue, string message);

 public static Angle FromDegrees(int degrees, double minutes)
 {
 if (minutes &lt; 0.0 || minutes &gt;= 60.0)
  ThrowArgumentOutOfRange(&quot;minutes&quot;, minutes, &quot;Argument must be positive and less than 60.&quot;);

 ...
 }

Then, on each platform-specific code, declare the body of the method.

For .NET 3.5 and above, pass all the arguments to the constructor:

 static partial void ThrowArgumentOutOfRange(string paramName, object paramValue, string message)
 {
   throw new ArgumentOutOfRangeException(paramName, paramValue, message);
 }

While for .NET Micro Framework the paramValue argument is ignored:

 static partial void ThrowArgumentOutOfRange(string paramName, object paramValue, string message)
 {
   throw new ArgumentOutOfRangeException(paramName, message);
 }

The partial keyword makes the method optional. No compile-time or run-time errors will result if the method is called but not implemented. If you want to make it mandatory, just remove the signature declaration and the partial keywords from the platform specific code.

Conclusion

The use of the partial keyword in shared projects is a clean and easy to maintain solution for handling platform specific code.
Featured image: “Spaceship Earth” by aalmada

ReactiveUI: Binding to an Android service the reactive way

ReactiveUI: Binding to an Android service the reactive way

ReactiveUI is a MVVM framework that takes full advantage of the Reactive Extensions for .NET (Rx.NET) where everything is a stream.

Together with Xamarin it’s possible to use it in the development of Android applications.

For these applications you may need to bind to a service so you can access its functionality. Binding is an asynchronous operation and, when using the Android API, the operation is based on callbacks. Xamarin simply wraps this process to .NET and not having anonymous classes in C# makes this implementation a bit of a mess.

The master branch for ReactiveUI version 7 (not yet released) includes a Context.ServiceBound() extension method that returns an IObservable<IBinder>.

public static IObservable<IBinder> ServiceBound (
this Context context, Intent intent, Bind flags = Bind.None)

public static IObservable<TBinder> ServiceBound<TBinder> (
this Context context, Intent intent, Bind flags = Bind.None) 
where TBinder: class, IBinder

Binding now only requires a couple of lines of code:

var intent = new Intent(context, typeof(DemoService));
var bind = context.ServiceBound (intent, Bind.AutoCreate);

Notice that unlike BindService() you don’t have to pass in an IServiceConnection, it is managed internally by the Observable. The connection is unbound automatically when the Observable subscription is disposed.

The returned value is an Observable so you can use async/await or use Rx.NET operators to filter and compose with other observables.

Featured image: “Electrifying sunset” by aalmada

NetFabric.Angle: type-safety and units-safety for angles

NetFabric.Angle: type-safety and units-safety for angles

Why would I need an Angle structure?

Angles are usually represented as a floating-point. Its value is usually in radians but some frameworks use degrees, or even both. Unity, for example, uses radians in Mathf but degrees in Transform. The only way to find this is by reading the documentation. There’s nothing explicit in the method declaration and the compiler cannot make any validation.

There is also nothing in Mathf, or in System.Math, that tells the developer or the compiler that Sin() takes an angle value and returns a floating-point value, while Asin() is the other way around.

NetFabric.Angle implements a .NET structure that represents an angle, giving you efficient type-safety and units-safety.

Type-safety

When using the Angle structure it’s possible to impose rules like “an angle can only be added to another angle”.

Looking at the method Asin below, you can immediately see that it returns an Angle. The compiler will throw an error when you try to assign it to anything else.

double Sin (Angle angle);
Angle Asin(double value);

You also don’t have to worry about what units were used to create the angle, simplifying code and documentation.

Units-safety

To create an Angle you have to explicitly specify the units you are using:

var right0 = Angle.FromRadian(Math.PI / 2.0);
var right1 = Angle.FromDegrees(90.0);
var right2 = Angle.FromGradian(100.0);

Same when you want to get the value:

var radians = angle.ToRadians();
var degrees = angle.ToDegrees();
var gradians = angle.ToGradians();

When using math operations, comparison, trigonometry, linear interpolation and so on, you don’t have to worry about units.

Cross-platform

Angles are used on any platform. From micro-controllers to data centers, passing by mobile devices and gaming consoles. NetFabric.Angle is available for as many .NET flavors as possible. It’s available as a Nuget package and as a .unitypackage.

The source project structure to achieve this is described on my previous post.

IDE integration

NetFabric.Angle comes with debugger visualization metadata that is recognized by Visual Studio. When stopped at a breakpoint and the mouse cursor hovering on an Angle variable, the DataTip shows the variable value in degrees and when expanded shows the value in all angle measurement units. This makes it easier to understand the value of the variable.

NetFabric.Angle.DataTip.Expanded

The Unity package includes extension methods for Unity classes like Quaternion, Transform, Vector2 and Vector3, allowing the direct use o Angle.

public class NewBehaviourScript : MonoBehaviour {

    [AngleDegreesRange(-180.0, 180.0)]
    public Angle RotateX;

    void Update () {
        transform.Rotate(Time.deltaTime * RotateX, Angle.Zero, Angle.Zero);
    }

}

The Unity package also contains a custom property drawer for Angle. For now, it contains the same slider and text box with the value in degrees as the existing floating-point angles but you gain type-safety and units-safety in the scripts. This custom drawer also takes into account the angle range metadata attribute that can be specified in any of the angle measurement units.

NetFabric.Angle.Unity

Featured image: “Angles” by Benjamin Horn