Changes

Jump to navigation Jump to search
expand
Line 1: Line 1:  
{{../../header}}
 
{{../../header}}
   −
===Reflection===
+
The reflection API lets you access fields, properties, or methods you otherwise couldn't access. You can use it from <tt>helper.Reflection</tt> in your entry method, or <tt>this.Helper.Reflection</tt> elsewhere in your entry class.
SMAPI provides an API for robustly accessing fields, properties, or methods you otherwise couldn't access, such as private fields. You can use it from <tt>helper.Reflection</tt> in your entry method, or <tt>this.Helper.Reflection</tt> elsewhere in your entry class.
     −
Here are a few examples of what this lets you do:
+
==Intro==
 +
[https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection ''Reflection''] is a powerful C# feature which lets code analyse and interact with code. SMAPI provides a simplified reflection API focused on accessing private game fields, properties, and methods. SMAPI will automatically handle validation, caching, and performance optimisation.
    +
==Basic reflection==
 +
===Overview===
 +
SMAPI provides three overloaded methods to access code: <tt>GetField</tt>, <tt>GetProperty</tt>, and <tt>GetMethod</tt>. Each one takes three arguments:
 +
{| class="wikitable"
 +
|-
 +
! argument
 +
! purpose
 +
|-
 +
| <tt>obj</tt> or <tt>type</tt>
 +
| The instance (or type if static) which has the private field/property/method you want to access.
 +
|-
 +
| <tt>name</tt>
 +
| The name of the private field/property/method.
 +
|-
 +
| <tt>required</tt>
 +
| Whether to throw a descriptive exception if the field/property/method isn't found. Default true. If set to false, it will return <tt>null</tt> instead and you should validate it yourself.
 +
|}
 +
 +
Each method returns an object you can use to interact further with it — like getting or setting the field/property value, or invoking the method.
 +
 +
===Fields and properties===
 +
<tt>GetField</tt> and <tt>GetProperty</tt> are used the same way. Both return an object with two methods: <tt>GetValue</tt> returns the current field/property value, and <tt>SetValue</tt> overrides their value.
 +
 +
You can get the value directly:
 
<source lang="c#">
 
<source lang="c#">
// did you pet your pet today?
+
// get value of instance field
 
bool wasPet = this.Helper.Reflection.GetField<bool>(pet, "wasPetToday").GetValue();
 
bool wasPet = this.Helper.Reflection.GetField<bool>(pet, "wasPetToday").GetValue();
 +
</source>
 +
 +
Or you can keep a reference to the reflection data, and change the value separately:
 +
<source lang="c#">
 +
// set value of static field
 +
IReflectedField<int> soundTimer = this.Helper.Reflection.GetField<int>(typeof(Junimo), "soundTimer");
 +
soundTimer.SetValue(100);
 +
</source>
   −
// what is the spirit forecast today?
+
If you need to access the field/property repeatedly, keeping the reflection object will improve performance.
 +
 
 +
===Methods===
 +
<tt>GetMethod</tt> returns an object with one overloaded method. The <tt>Invoke</tt> comes in two forms:
 +
* <tt>Invoke()</tt> calls a method with no return value.
 +
* <tt>Invoke&lt;T&gt;()</tt> calls a method which returns a value, where <tt>T</tt> is the expected return type.
 +
 
 +
For example, this calls the private <tt>TV.getFortuneForecast</tt> method and stores the value it returns:
 +
<source lang="c#">
 
string forecast = this.Helper.Reflection
 
string forecast = this.Helper.Reflection
 
   .GetMethod(new TV(), "getFortuneForecast")
 
   .GetMethod(new TV(), "getFortuneForecast")
 
   .Invoke<string>();
 
   .Invoke<string>();
 +
</source>
   −
// randomise the mines
+
If the method expects arguments, you can just add those to the <tt>Invoke</tt> method:
if(Game1.currentLocation is MineShaft)
+
<source lang="c#">
  this.Helper.Reflection.GetField<Random>(Game1.currentLocation, "mineRandom").SetValue(new Random());
+
Vector2 spawnTile = new Vector2(25, 25);
 +
this.helper.Reflection
 +
  .GetMethod(Game1.getFarm(), "doSpawnCrow")
 +
  .Invoke(spawnTile);
 
</source>
 
</source>
   −
This works with static or instance fields/methods, caches the reflection to improve performance, and will throw useful errors automatically when reflection fails.
+
==Advanced reflection==
 
+
If you need to do more, you can access the underlying C# reflection types:
If you need to do more, you can switch to [https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection C#'s underlying reflection API]:
  −
 
   
<source lang="c#">
 
<source lang="c#">
 
FieldInfo field = this.Helper.Reflection.GetField<string>(…).FieldInfo;
 
FieldInfo field = this.Helper.Reflection.GetField<string>(…).FieldInfo;
 
MethodInfo method = this.Helper.Reflection.GetMethod(…).MethodInfo;
 
MethodInfo method = this.Helper.Reflection.GetMethod(…).MethodInfo;
 
</source>
 
</source>
 +
 +
Or even use [https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection C# reflection] directly. Note that SMAPI adds caching and optimisations which you'll need to handle yourself if you do this.
translators
8,403

edits

Navigation menu