Changes

Jump to navigation Jump to search
m
Text replacement - "tt>" to "samp>"
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 <samp>helper.Reflection</samp> in your entry method, or <samp>this.Helper.Reflection</samp> 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.
   −
<source lang="c#">
+
==Basic reflection==
// did you pet your pet today?
+
===Overview===
 +
SMAPI provides three overloaded methods to access code: <samp>GetField</samp>, <samp>GetProperty</samp>, and <samp>GetMethod</samp>. Each one takes three arguments:
 +
{| class="wikitable"
 +
|-
 +
! argument
 +
! purpose
 +
|-
 +
| <samp>obj</samp> or <samp>type</samp>
 +
| The instance (or type if static) which has the private field/property/method you want to access.
 +
|-
 +
| <samp>name</samp>
 +
| The name of the private field/property/method.
 +
|-
 +
| <samp>required</samp>
 +
| Whether to throw a descriptive exception if the field/property/method isn't found. Default true. If set to false, it will return <samp>null</samp> 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===
 +
<samp>GetField</samp> and <samp>GetProperty</samp> are used the same way. Both return an object with two methods: <samp>GetValue</samp> returns the current field/property value, and <samp>SetValue</samp> overrides their value.
 +
 
 +
You can get the value directly:
 +
<syntaxhighlight lang="c#">
 +
// 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();
 +
</syntaxhighlight>
   −
// what is the spirit forecast today?
+
Or you can keep a reference to the reflection data, and change the value separately:
 +
<syntaxhighlight lang="c#">
 +
// set value of static field
 +
IReflectedField<int> soundTimer = this.Helper.Reflection.GetField<int>(typeof(Junimo), "soundTimer");
 +
soundTimer.SetValue(100);
 +
</syntaxhighlight>
 +
 
 +
If you need to access the field/property repeatedly, keeping the reflection object will improve performance.
 +
 
 +
===Methods===
 +
<samp>GetMethod</samp> returns an object with one overloaded method. The <samp>Invoke</samp> comes in two forms:
 +
* <samp>Invoke()</samp> calls a method with no return value.
 +
* <samp>Invoke&lt;T&gt;()</samp> calls a method which returns a value, where <samp>T</samp> is the expected return type.
 +
 
 +
For example, this calls the private <samp>TV.getFortuneForecast</samp> method and stores the value it returns:
 +
<syntaxhighlight lang="c#">
 
string forecast = this.Helper.Reflection
 
string forecast = this.Helper.Reflection
 
   .GetMethod(new TV(), "getFortuneForecast")
 
   .GetMethod(new TV(), "getFortuneForecast")
 
   .Invoke<string>();
 
   .Invoke<string>();
 +
</syntaxhighlight>
   −
// randomise the mines
+
If the method expects arguments, you can just add those to the <samp>Invoke</samp> method:
if(Game1.currentLocation is MineShaft)
+
<syntaxhighlight lang="c#">
  this.Helper.Reflection.GetField<Random>(Game1.currentLocation, "mineRandom").SetValue(new Random());
+
Vector2 spawnTile = new Vector2(25, 25);
</source>
+
this.helper.Reflection
 
+
  .GetMethod(Game1.getFarm(), "doSpawnCrow")
This works with static or instance fields/methods, caches the reflection to improve performance, and will throw useful errors automatically when reflection fails.
+
  .Invoke(spawnTile);
 
+
</syntaxhighlight>
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#">
+
==Advanced reflection==
 +
If you need to do more, you can access the underlying C# reflection types:
 +
<syntaxhighlight 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>
+
</syntaxhighlight>
 +
 
 +
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.
105,728

edits

Navigation menu