However PCL's target specific platforms that you want to target and letting the IDE code to those restrictions however this results in you only being able to use a subset of features.
PCL's use Profiles to determine which apis you have access too, not all platforms share all api's so each time you add a platform to your PCL you potentials restrict the API's you can call to the intersection point of all the selected platforms. Meaning you should target as few platforms as possible.
.Net Standard libraries
With all these restrictions on PCL's in come .Net Standard libraries which create a standard way of each platform to implement it's api's meaning that you no longer get a subset based on platform..net standard libraries has multple versions and will continue to grow as new platforms are added and existing ones evolve. each new version is a superset of the previous versions apis
Version | API's |
---|---|
1.0 | Primitives, reflection, tasks, collections, LINQ to Objects, XML, etc |
1.1 | Concurrent collections, interop, Http interactions, etc |
1.2 | Threading timer, interop+, etc |
1.3 | Console, File System, Trehad pool, sockets, cryptography, etc |
1.4 | Crypto+, etc |
1.5 | Assembly members+, streams+, etc |
1.6 | crypto+, regex+, expressions compiling, etc |
2.0 | Data Classes, Drawing, pipes, caching, SMTP, web sockets, serialization+ XPath, etc |
Now so far it looks like the best option is to pick the latest .net standard library, well that would be nice, but negative. you have to align your .net standard library version with the version that is supported in your platform, so for example .net Framework v4.5.1 supports the .net standard v1.2 and thus you can only use the 1.2 version of .net standard at a max.
thus it comes down to a balancing act of .net standard features you want vs platforms you want to support with your library.
.NET Standard | PCL Profile |
---|---|
1.2 | Profile 151 (.NET Framework 4.5.1, Windows 8.1, WP 8.1 |
1.2 | Profile 44 (.NET Framework 4.5.1, Windows 8.1) |
1.1 | Profile 111 (.NET Framework 4.5, Windows 8, WP8.1) |
1.1 | Profile 7 (.NET Framework 4.5, Windows 8) |
1.0 | Profile 259 (.NET Framework 4.5, Windows 8, WP8.1, WP 8) |
1.0 | Profile 78 (.NET Framework 4.5, Windows 8, WP 8) |
The chart above depicts two way compatibility, meaning that in .net std 1.2 we can use a pcl profile 151 and vice versa.
Because PCL's and .net standard libraries are shared at the binary level, many of the platform specific API's can't be added to the project directly. There are strategies to mitigate this restriction such as:
Leverage platform specific api's in platform projects, then pass the results to your library.
using System;
namespace pav.pclExample {
class Person {
public string FullName { get; set; }
public DateTime BirthDate { get; set; }
public Func<Person, int> GetAge { get; set; }
public string
DispalyPerson()
{
int age = -1;
if (GetAge != null
&& (age = GetAge(this)) >
-1)
return $"{FullName} is {age} years old";
return $"{FullName} was born {BirthDate.ToShortDateString()}";
}
}
class Program {
static void Main(string[] args) {
var getAgeFunc = new Func<Person, int>(p => {
var today = DateTime.Today;
var age = today.Year - p.BirthDate.Year;
if (p.BirthDate > today.AddYears(-age))
age--;
return age;
});
var p1 = new Person {
FullName = "Pawel Ciucias",
BirthDate = new DateTime(1984, 1, 31)
};
var p2 = new Person {
FullName = "Magda Tywoniuk",
BirthDate = new DateTime(1984, 6, 28),
GetAge = getAgeFunc
};
Console.WriteLine(p1.DispalyPerson());
Console.WriteLine(p2.DispalyPerson());
}
}
}
In a way the above itself is a kind of Dependency injection, however we'd have to pass our func to each instance of our Person class, but take a look at the following
using System;
namespace pav.pclExample {
class Person {
public string FullName
{ get; set; }
public DateTime BirthDate { get; set; }
static Func<Person, int> GetAge { get; set; }
public Person() { }
public Person(Func<Person, int> getAge) : this() => GetAge = getAge;
public string
DispalyPerson()
{
int age = -1;
if (GetAge != null
&& (age = GetAge(this)) >
-1)
return $"{FullName} is {age} years old";
return $"{FullName} was born {BirthDate.ToShortDateString()}";
}
}
class Program {
static void Main(string[] args) {
var getAgeFunc = new Func<Person, int>(p => {
var today = DateTime.Today;
var age = today.Year - p.BirthDate.Year;
if (p.BirthDate > today.AddYears(-age))
age--;
return age;
});
var p1 = new
Person(getAgeFunc) {
FullName = "Pawel Ciucias",
BirthDate = new DateTime(1984, 1, 31)
};
var p2 = new Person {
FullName = "Magda Tywoniuk",
BirthDate = new DateTime(1984, 6, 28)
};
Console.WriteLine(p1.DispalyPerson());
Console.WriteLine(p2.DispalyPerson());
}
}
}
This is starting to feel more and more like a modern code sharing strategy and not just a haphazard hack.