Skip to content

Fody/InfoOf

Repository files navigation

InfoOf.Fody

Chat on Gitter NuGet Status

Provides methodof, propertyof and fieldof equivalents of typeof.

See Milestones for release notes.

This is an add-in for Fody

It is expected that all developers using Fody become a Patron on OpenCollective. See Licensing/Patron FAQ for more information.

Usage

See also Fody usage.

NuGet installation

Install the InfoOf.Fody NuGet package and update the Fody NuGet package:

PM> Install-Package Fody
PM> Install-Package InfoOf.Fody

The Install-Package Fody is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.

Add to FodyWeavers.xml

Add <InfoOf/> to FodyWeavers.xml

<Weavers>
  <InfoOf/>
</Weavers>

Input Code

var type = Info.OfType("AssemblyName", "MyClass");

var method = Info.OfMethod("AssemblyName", "MyClass", "MyMethod");
var methodTyped = Info.OfMethod<MyClass>("MyMethod");

var constructor = Info.OfConstructor("AssemblyName", "MyClass");
var constructorTyped = Info.OfConstructor<MyClass>();

var getProperty = Info.OfPropertyGet("AssemblyName", "MyClass", "MyProperty");
var getPropertyTyped = Info.OfPropertyGet<MyClass>("MyProperty");

var setProperty = Info.OfPropertySet("AssemblyName", "MyClass", "MyProperty");
var setPropertyTyped = Info.OfPropertySet<MyClass>("MyProperty");

var field = Info.OfField("AssemblyName", "MyClass", "myField");
var fieldTyped = Info.OfField<MyClass>("myField");

var getIndexer = Info.OfIndexerGet("AssemblyName", "MyClass", "Int32");
var getIndexerTyped = Info.OfIndexerGet<MyClass>("Int32");

var setIndexer = Info.OfIndexerSet("AssemblyName", "MyClass", "Int32");
var setIndexerTyped = Info.OfIndexerSet<MyClass>("Int32");

snippet source | anchor

What gets compiled

var type = typeof(MyClass);

var method = methodof(MyClass.MyMethod);
var methodTyped = methodof(MyClass.MyMethod);

var constructor = methodof(MyClass..ctor);
var constructorTyped = methodof(MyClass..ctor);

var getProperty = methodof(MyClass.get_MyProperty);
var getPropertyTyped = methodof(MyClass.get_MyProperty);

var setProperty = methodof(MyClass.set_MyProperty);
var setPropertyTyped = methodof(MyClass.set_MyProperty);

var field = fieldof(MyClass.myField);
var fieldTyped = fieldof(MyClass.myField);

var getIndexer = methodof(MyClass.get_Item);
var getIndexerTyped = methodof(MyClass.get_Item);

var setIndexer = methodof(MyClass.set_Item);
var setIndexerTyped = methodof(MyClass.set_Item);

snippet source | anchor

Specifying Parameters

  • Parameters are specified with their full type name
  • For methods, when no parameters are specified, the method with the least number of parameters will be returned.
  • Parameters are specified as comma separated list, e.g. "System.String, System.Int32".
  • For every parameter, the first namespace part can be omitted, so "String, Int32" works, too.
  • Nested namespaces are not handled, only "Regex" won't work, but "System.Text.RegularExpressions.Regex" or "Text.RegularExpressions.Regex".

Finding Generic Methods

Actually there is no specific support for generic methods. Both "Method()" and "Method<T>()" will be found by the name Method.

If there is a non-generic method and a generic overload with the same parameter signature, only the first of both will be returned.

Specifying Generic Types in Method Parameters

The typeName parameter of the Info.Of* methods use the following BNF grammar:

<fullTypeSpec> ::= <typeName> [<genericSpec>]
<genericSpec>  ::= "<" <genericTypes> ">"
<genericTypes> ::= <genericType> ["," <genericTypes>]
<genericType>  ::= <fullTypeSpec>
<name>         ::= <identifier> [<name>]
<typeName>     ::= <name>
<assemblyName> ::= <name>
<identifier>   ::= <letter> | <digit> | <specialChar>
<letter>       ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" |
                   "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" |
                   "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" |
                   "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
<digit>        ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
<specialChar>  ::= "." | "`"

To specify a Dictionary<int, string>, the typeName would be System.Collections.Generic.Dictionary`2<System.Int32,System.String>.

Specifying Generic Parameter Types in Generic Methods

To find a method with the following signature

MyMethod<T>(T param)

use

Info.OfMethod<MyClass>("MyMethod", "T")

Escape Sequences

If the following chars are part of the typeName, they will need to be escaped with a "\": "\", "<", ">", "|", ",".

Also, whitespace is ignored by default, so they also need to be escaped with "\" if part of the typeName.

Why not use Expressions

It would also be possible to define members and types using a combination of generics and expressions. This would allow for intellisense at code time. The problem with expressions are as follows

  1. The conversion of expression to "infoof"s is significantly more complex. Making the solution more complex and having a negative impact on compile performance.
  2. Expressions cannot represent non public types or members.
  3. It is difficult to represent all combinations of members using expressions.

But it is not strong typed

Actually it is strong typed, it does not have intellisense. If any of the strings passed into Info do not map to a type or member it will log a build error and stop the build.

Icon

Information designed by Phil Goodwin from The Noun Project.