--- title: "User guide" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{User guide} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} library(rSharp) knitr::opts_chunk$set( collapse = TRUE, comment = "#>", # The examples below call into .NET. When no runtime is available (for example # on CRAN or R-universe build machines without .NET) the code is shown but not # executed, so the vignette still builds. eval = dotnetAvailable() ) ``` The `{rSharp}` package is a low-level interoperability bridge between R and the .NET runtime. It offers access to .NET libraries from R, allowing to create .NET objects, access their fields, and call their methods. `{rSharp}` uses R's native interface to C++ which then calls the .NET runtime. To make working with .NET objects intuitive, `{rSharp}` utilizes the R6 object-oriented approach. This article will guide you through the basic usage of the package, starting from loading an assembly to calling methods and accessing fields. ## Data type conversion ### Basic data types `{rSharp}` allows to call public and static methods implemented in .NET assemblies and to pass data to these methods. In return, calls will often result in some data being returned. Where there is an obvious and natural conversion between R and .NET data types, R values can be easily passed as is, and the return value can be interpreted as a native R type. Most of the basic modes in R (`character`,`numeric`,`integer`, `logical`, and their vectors) have equivalents in .NET. ### Object pointers and the `NetObject` class It is common that .NET method returns another object that can not be directly converted to a native R type. In such cases, the method returns an external pointer to the .NET object. The `NetObject` class is used to represent such pointers in R. The `NetObject` class is an R6 object that wraps the external pointer and provides methods to interact with the .NET object with semantics familiar for the object-oriented programming. In most cases, the user would prefer to interact with these objects through the `NetObject`. If, for any reason, the user needs to access the raw pointer, it can be done by accessing the field `$pointer` of the R6 object. `NetObject` instances can be passed to .NET methods as arguments. If a method expects an array of .NET objects, all objects within the R list must be instances of `NetObject`. Mixed lists of native R types and `NetObject` instances are not supported. Furthermore, the type of the array will be interpreted as `Object[]`. ## Loading an assembly To start working with a .NET assembly, you need to load it into the R session. This is done using the `rSharp::loadAssembly` function. In this example, we will use the library `rSharp.Examples.dll` provided with this package. ```{r} library(rSharp) assembly <- loadAssembly(system.file("extdata", "rSharp.Examples.dll", package = "rSharp")) ``` We can get the list of all loaded assemblies using the `getLoadedAssemblies` function: ```{r} getLoadedAssemblies() # Check if a specific assembly is loaded isAssemblyLoaded("rSharp.Examples") ``` ## Working with static members Static members of a .NET class can be accessed without creating an instance of the class. The `rSharp.Examples` library implements a class `SampleStaticClass` with different static members. To get an overview of the available static members of a class, you can use the `getStaticMembers` function: ```{r} getStaticMembers("rSharp.Examples.SampleStaticClass") ``` To explore the methods, fields, and properties of a class separately, you can use the `getStaticMethods`, `getStaticFields`, and `getStaticProperties` functions, respectively. To call static methods of a .NET class, you can use the `rSharp::callStatic` function. If you are not sure which arguments are expected by a certain method, you can use the `getStaticMemberSignature` function to get the method's signature: ```{r} # Get the signature of the GetAString method getStaticMemberSignature("rSharp.Examples.SampleStaticClass", "GetAString") # Get the signature of the Add method getStaticMemberSignature("rSharp.Examples.SampleStaticClass", "Add") ``` The method `GetAString` returns a string and does not have any arguments, so we can call it using the `callStatic` function: ```{r} rSharp::callStatic("rSharp.Examples.SampleStaticClass", "GetAString") ``` If the method has arguments, they can be passed as additional arguments to the `callStatic` function, as in this case of calling the static function `Add()` that requires two integers as arguments: ```{r} rSharp::callStatic("rSharp.Examples.SampleStaticClass", "Add", as.integer(1), as.integer(2)) ``` Mind that we had to explicitly cast the arguments to integer, as all numeric values in R are double by default. We can also access static fields of a class using the `getStatic` and `setStatic` functions: ```{r} # Get the value of the static field getStatic("rSharp.Examples.SampleStaticClass", "StaticString") ``` This static string is returned by the method `GetAString` we called earlier. We can set the value of the static field and would expect it to be returned by the `GetAString` method: ```{r} # Set the value of the static field setStatic("rSharp.Examples.SampleStaticClass", "StaticString", "New value") # Get the value of the static field getStatic("rSharp.Examples.SampleStaticClass", "StaticString") # Call the GetAString method callStatic("rSharp.Examples.SampleStaticClass", "GetAString") ``` ## Working with objects So far, we were passing basic data types as arguments and receiving basic data types as return values. However, in many cases, we would need to work with .NET objects. For example, the method `GetInstanceObject` returns an instance of the `SampleInstanceClass` class. `rSharp` wraps such objects in the `NetObject` class: ```{r} # Call the SampleInstanceClass method instance <- callStatic("rSharp.Examples.SampleStaticClass", "GetInstanceObject") # Check the class of the returned object class(instance) # Get the type of the object instance$type ``` The R6 class `NetObject` holds the pointer to the .NET object and provides methods to interact with it. Thus, the constructor of `NetObject` requires an external pointer to a .NET object. The pointer can be created by calling the `newPointerFromName` function. Is the constructor of the .NET class requires arguments, they can be passed as additional arguments to the `newPointerFromName` function. We can examine the constructors of a class with the `getConstructors` function: ```{r} getConstructors("rSharp.Examples.SampleInstanceClass") ``` The `SampleInstanceClass` has six different constructor signature. We can create a new pointer to an instance of the class by calling the constructor with two double arguments and create a `NetObject` using the pointer: ```{r} # Create a pointer to a .NET object pointer <- newPointerFromName("rSharp.Examples.SampleInstanceClass", as.double(1), as.double(2)) # Wrap the pointer in a new NetObject newInstance <- NetObject$new(pointer) ``` The more conventient way of creating a `NetObject` for a new object is by calling the `newObjectFromName` function: ```{r} # Create a new instance of the SampleInstanceClass newInstance2 <- newObjectFromName("rSharp.Examples.SampleInstanceClass", as.integer(1)) ``` As with static classes, we can examine the methods, fields, and properties of an object using the `getMethods`, `getFields`, and `getProperties` methods called on the object, respectively. ```{r} # Get the methods of the object newInstance$getMethods() # Get the fields of the object newInstance$getFields() # Get the properties of the object newInstance$getProperties() ``` A non-static class can also have static members, which can be listed using the `getStatic...` functions: ```{r} newInstance$getStaticFields() ``` Setting and getting non-static fields and properties of an object is done using the `set` and `get` methods, respectively: ```{r} # Get the value of the static field: newInstance$get("FieldDoubleOne") # Change the value of the static field: newInstance$set("FieldDoubleOne", 23) # Get the value of the static field: newInstance$get("FieldDoubleOne") ``` To get or set static fields, use the functions `getStatic` and `setStatic`. Finally, we can call the methods of the object using the `call` method: ```{r} # Call the Add method newInstance$call("GetAString") ```