I have a Conversion class like this:
public class Conversion
{
public memorySource MSource { get; set; }
public Rule[] Rules { get; set; }
public Conversion(XElement xElement)
{
// I use Rules property here
// From Rules property and xElement parameter i initialize MSource Property
}
public Conversion(ExcelPackage)
{
// Also i use Rules property here
// From Rules property and xElement parameter i initialize MSource Property
}
}
When I want to construct an instance of the Conversion class, I do this:
Conversion cvr = new Conversion(xElement) { Rules = rules };
Then I get this error:
Object reference not set to an instance of an object
I know that construction of object begins before initializing of properties, but is there a way to do inverse?
I can use Rules property as parameter of constructor but it is not suitable for performance because I have several constructors.
Yes, simply pass the value as parameter in the constructor. It's the only way:
Conversion cvr = new Conversion(rules, package);
Where the constructor is:
public Conversion(Rule[] rules, ExcelPackage package)
{
this.Rules = rules;
...
}
You can default the rules parameter for your other constructor, so you don't have to duplicate code:
public Conversion(ExcelPackage package) : this(new Rule[] { ... }, package)
{
...
}
Related
I have the following simple classes :
public abstract class GitObject
{
public Repository Repository { get; set; }
public abstract string Serialize();
public abstract void Deserialize(string data);
public class Blob : GitObject
{
public string Data { get; set; }
public Blob(Repository repository, string data = null)
{
if (data != null) Data = File.ReadAllText(data);
Repository = repository;
}
public override string Serialize()
{
return JsonSerializer.Serialize(this);
}
public override void Deserialize(string data)
{
Blob blobData = JsonSerializer.Deserialize<Blob>(data);
}
}
}
I know there is probably a LOT of room for improvement ( and I a am happy to hear about it ). However, the method Deserialize gives me the error
Each parameter in the deserialization constructor on type 'CustomGit.Repository'
must bind to an object property or field on deserialization. Each parameter name must
match with a property or field on the object. The match can be case-insensitive.
For testing if this method works as intended I use this approach (which also throws the error)
FileInfo file = new FileInfo(Path.Combine(repository.GitDirectory.FullName, "code.txt"));
GitObject.Blob firstBlob = new GitObject.Blob(repository, file.FullName);
var json = firstBlob.Serialize();
GitObject.Blob secondBlob = new GitObject.Blob(repository);
secondBlob.Deserialize(json);
What am I doing wrong and what should I change in general?
You are encountering two separate problems related to deserializing types with parameterized constructors. As explained in the documentation page How to use immutable types and non-public accessors with System.Text.Json:
System.Text.Json can use a public parameterized constructor, which makes it possible to deserialize an immutable class or struct. For a class, if the only constructor is a parameterized one, that constructor will be used. For a struct, or a class with multiple constructors, specify the one to use by applying the [JsonConstructor] attribute. When the attribute is not used, a public parameterless constructor is always used if present. The attribute can only be used with public constructors.
...
The parameter names of a parameterized constructor must match the property names. Matching is case-insensitive, and the constructor parameter must match the actual property name even if you use [JsonPropertyName] to rename a property.
Your first problem is with the type Repository. You don't show it in your question, but I assume it looks something like this:
public class Repository
{
public Repository(string gitDirectory) => this.GitDirectory = new DirectoryInfo(gitDirectory);
[JsonConverter(typeof(DirectoryInfoConverter))]
public DirectoryInfo GitDirectory { get; }
}
public class DirectoryInfoConverter : JsonConverter<DirectoryInfo>
{
public override DirectoryInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
new DirectoryInfo(reader.GetString());
public override void Write(Utf8JsonWriter writer, DirectoryInfo value, JsonSerializerOptions options) =>
writer.WriteStringValue(value.ToString());
}
If so, your problem here is that either the name of the constructor argument corresponding to GitDirectory is not the same as the property name or the type of the argument is not the same. Because, as it turns out, there is an undocumented restriction that types of the constructor arguments and corresponding properties must also match exactly. For confirmation see JsonConstructor fails on IEnumerable property?.
Demo fiddle #1 here.
To fix this, you must either:
Add a public parameterless constructor and make Repository be mutable (i.e. add a setter for GitDirectory), or
Add a constructor with an argument of the same type and name as the property GitDirectory, and mark it with [JsonConstructor].
Adopting option #2, your Repository type should now look like:
public class Repository
{
public Repository(string gitDirectory) => this.GitDirectory = new DirectoryInfo(gitDirectory);
[JsonConstructor]
public Repository(DirectoryInfo gitDirectory) => this.GitDirectory = gitDirectory ?? throw new ArgumentNullException(nameof(gitDirectory));
[JsonConverter(typeof(DirectoryInfoConverter))]
public DirectoryInfo GitDirectory { get; }
}
And now Respository will deserialize successfully. Demo fiddle #2 here.
However, you will now encounter your second problem, namely that the Blob type will not round-trip either. In this case, Blob does have a unique parameterized constructor whose argument names and types correspond precisely to properties -- but the semantics of one of them, data, are completely different:
public class Blob : GitObject
{
public string Data { get; set; }
public Blob(Repository repository, string data = null)
{
if (data != null)
Data = File.ReadAllText(data);
Repository = repository;
}
The property Data corresponds to the textual contents of a file, while the argument data corresponds to the file name of a file. Thus when deserializing Blob your code will attempt to read a file whose name equals the file's contents, and fail.
This inconsistency is, in my opinion, poor programming style, and likely to confuse other developers as well as System.Text.Json. Instead, consider adding factory methods to create a Blob from a file, or from file contents, and remove the corresponding constructor argument. Thus your Blob should look like:
public class Blob : GitObject
{
public string Data { get; set; }
public Blob(Repository repository) => this.Repository = repository ?? throw new ArgumentNullException(nameof(repository));
public static Blob CreateFromDataFile(Repository repository, string dataFileName) =>
new Blob(repository)
{
Data = File.ReadAllText(dataFileName),
};
public static Blob CreateFromDataConents(Repository repository, string data) =>
new Blob(repository)
{
Data = data,
};
public override string Serialize() => JsonSerializer.Serialize(this);
public override void Deserialize(string data)
{
// System.Text.Json does not have a Populate() method so we have to do it manually, or via a tool like AutoMapper
Blob blobData = JsonSerializer.Deserialize<Blob>(data);
this.Repository = blobData.Repository;
this.Data = blobData.Data;
}
}
And you would construct and round-trip it as follows:
var firstBlob = GitObject.Blob.CreateFromDataFile(repository, file.FullName);
var json = firstBlob.Serialize();
var secondBlob = new GitObject.Blob(repository);
secondBlob.Deserialize(json);
Final working demo fiddle here.
Maybe a bit of a philosophical, but should I generally, in C#, expect a property with an interface type to retain its assigned class?
For example a class property like this:
public IBehavior Behavior { get; set; }
that gets assigned a implmented class
instance.Behavior = new ImplementedBehavior();
should i generally expect this cast to succeed?
Assert.IsNotNull(instance.Behavior as ImplementedBehavior);
Or is it lost, and i should have kept a reference to the instantiated ImplementedBehavior instead?
var ib = new ImplementedBehavior();
instance.Behavior = ib;
Assert.IsNotNull(ib as ImplementedBehavior);
Clarification:
Looking to use an external class, that when assigned a class similar to what is done above, changes the content of instance.Behavior internally after i set it, to return an instance of SomeOtherBehavior
Can I assume this is bad behavior and not in the spirit of C#?
GitHub issue related to the question:
https://github.com/aws/aws-cdk/issues/19013
Can I expect a property to retain its assigned class?
No.
It may not even be accepted in the first place, e.g.
public IBehavior Behavior {
get {...}
set { if (!value.HasCapabilityX) return; ... };
}
It may simply be changed by some other code that has access to the same object (instance):
Thread A:
instance.Behavior = new ImplementationA();
Thread B:
instance.Behavior = new ImplementationB();
There are object oriented design patterns that promote the idea of changing the object, e.g. the decorator pattern and proxy pattern, e.g.
public IBehavior Behavior {
get {...}
set { _behavior = new PermissionsDecorator(new LoggingDecorator(value)); };
}
Should I generally expect this cast to succeed?
No.
Or is it lost [...] ?
Maybe.
and I should have kept a reference to the instantiated ImplementedBehavior instead?
If you need something that ImplementedBehavior can do which IBehavior can't do, then yes, keep a reference.
Can I assume this is bad behavior and not in the spirit of C#?
This is not bad behavior. It is good practice to only expect the interface to fulfill the contract that was specified by the interface.
The property can be assigned any value that implements IBehavior:
public class BehaviorA : IBehavior { }
public class BehaviorB : IBehavior { }
...
instance.Behavior = new BehaviorA();
instance.Behavior = new BehaviorB();
So in general, you CANNOT expect a property's value to be of a particular type that derives from the property's declared type.
this
Assert.IsNotNull(instance.Behavior as ImplementedBehavior);
will work so long as you dont change instance.Behavior, it wont happen spontaneously. But if you also have
class Imp2 : IBehavior{
}
and then do
instance.Behavior = new Imp2();
then that assert will fail.
Let me give you a simple example using
public interface IType
{
string Text { get; set; }
int Number { get; set; }
}
public class MyClass
{
public IType MyProperty { get; set; }
}
public class MyFirstType : IType
{
public string Text { get; set; }
public int Number { get; set; }
public char Letter { get; set; }
}
public class MySecondType : IType
{
public string Text { get; set; }
public int Number { get; set; }
public decimal Price { get; set; }
}
And here is the usage with comments that explain what's going on:
var instance = new MyClass(); // creates a new object of type "MyClass" in memory
var myFirstType = new MyFirstType(); // creates a new object of type "MyFirstType" in memory
var mySecondType = new MySecondType(); // creates a new object of type "MySecondType" in memory
// setting property
instance.MyProperty = myFirstType; // MyProperty is now simply referencing "myFirstType" using the address to its position in memory
instance.MyProperty = mySecondType; // The memory address to "myFirstType" is now replaced by the memory address to "mySecondType"
// printing values of MyProperty
Console.WriteLine(instance.MyProperty.Text); // will work.
Console.WriteLine(instance.MyProperty.Number); // will work.
Console.WriteLine(instance.MyProperty.Letter); // compiler error. IType does not specify this "Letter" property.
Console.WriteLine(instance.MyProperty.Price); // compiler error. IType does not specify this "Price" property. However, it is in memory on the object, but the compiler will not infer the type of the property unless the property is explicitly casted.
// casting
var convertedPropertyA = (MyFirstType)instance.MyProperty; // throws an exception since the property is currently set to an object of type "MySecondType".
var convertedPropertyB = (MySecondType)instance.MyProperty; // works because the property is set to an object of type "MySecondType".
// modifying the values of the property
instance.MyProperty.Text = "new text"; // this works
instance.MyProperty.Number = 123; // this works
((MySecondType)instance.MyProperty).Price = 14.99; // this works
Console.WriteLine(instance.MyProperty.Text); // prints "new text"
Console.WriteLine(instance.MyProperty.Number); // prints "123"
Console.WriteLine(((MySecondType)instance.MyProperty).Price); // prints "14.99"
When using a specific .ctor via JsonConstructor for deserializing IList<ISomeInterface> properties, the parameter names must match the original Json names and the JsonProperty mapping on those properties are not used.
Example:
SpokenLanguages parameter is always null since it does not match spoken_languages, but there is a JsonProperty mapping it:
public partial class AClass : ISomeBase
{
public AClass() { }
[JsonConstructor]
public AClass(IList<SysType> SysTypes, IList<ProductionCountry> production_countries, IList<SpokenLanguage> SpokenLanguages)
{
this.Genres = SysTypes?.ToList<IGenre>();
this.ProductionCountries = production_countries?.ToList<IProductionCountry>();
this.SpokenLanguages = SpokenLanguages?.ToList<ISpokenLanguage>();
}
public int Id { get; set; }
public IList<IGenre> Genres { get; set; }
[JsonProperty("production_countries")]
public IList<IProductionCountry> ProductionCountries { get; set; }
[JsonProperty("spoken_languages")]
public IList<ISpokenLanguage> SpokenLanguages { get; set; }
}
Is this just a "limitation" of how Json.Net calls the constructor or is there something I am missing.
FYI: I am code generating all this via Rosyln and am not looking at generating a JsonConverter for each type for this...
When Json.NET invokes a parameterized constructor, it matches JSON properties to constructor arguments by name, using an ordinal case-ignoring match. However, for JSON properties that also correspond to type members, which name does it use - the member name, or the override type member name specified by JsonPropertyAttribute.PropertyName?
It appears you are hoping it matches on both, since your argument naming conventions are inconsistent:
The constructor argument production_countries matches the overridden property name:
[JsonProperty("production_countries")]
public IList<IProductionCountry> ProductionCountries { get; set; }
The constructor argument IList<SpokenLanguage> SpokenLanguages matches the reflected name rather than the overridden property name:
[JsonProperty("spoken_languages")]
public IList<ISpokenLanguage> SpokenLanguages { get; set; }
IList<SysType> SysTypes matches neither (is this a typo in the question?)
However, what matters is the property name in the JSON file itself and the constructor argument name as shown in JsonSerializerInternalReader.ResolvePropertyAndCreatorValues(). A simplified version of the algorithm is as follows:
The property name is read from the JSON file.
A closest match constructor argument is found (if any).
A closest match member name is found (if any).
If the JSON property matched a constructor argument, deserialize to that type and pass into the constructor,
But if not, deserialize to the appropriate member type and set the member value after construction.
(The implementation becomes complex when a JSON property matches both and developers expect that, for instance, [JsonProperty(Required = Required.Always)] added to the member should be respected when set in the constructor.)
Thus the constructor argument production_countries will match a value named "production_countries" in the JSON, while the constructor argument SpokenLanguages will not match a JSON value named "spoken_languages".
So, how to deserialize your type successfully? Firstly, you could mark the constructor parameters with [JsonProperty(overrideName)] to override the constructor name used during deserialization:
public partial class AClass : ISomeBase
{
public AClass() { }
[JsonConstructor]
public AClass([JsonProperty("Genres")] IList<SysType> SysTypes, IList<ProductionCountry> production_countries, [JsonProperty("spoken_languages")] IList<SpokenLanguage> SpokenLanguages)
{
this.Genres = SysTypes == null ? null : SysTypes.Cast<IGenre>().ToList();
this.ProductionCountries = production_countries == null ? null : production_countries.Cast<IProductionCountry>().ToList();
this.SpokenLanguages = SpokenLanguages == null ? null : SpokenLanguages.Cast<ISpokenLanguage>().ToList();
}
Secondly, since you seem to be using the constructor to deserialize items in collections containing interfaces as concrete objects, you could consider using a single generic converter based on CustomCreationConverter as an ItemConverter:
public partial class AClass : ISomeBase
{
public AClass() { }
public int Id { get; set; }
[JsonProperty(ItemConverterType = typeof(CustomCreationConverter<IGenre, SysType>))]
public IList<IGenre> Genres { get; set; }
[JsonProperty("production_countries", ItemConverterType = typeof(CustomCreationConverter<IProductionCountry, ProductionCountry>))]
public IList<IProductionCountry> ProductionCountries { get; set; }
[JsonProperty("spoken_languages", ItemConverterType = typeof(CustomCreationConverter<ISpokenLanguage, SpokenLanguage>))]
public IList<ISpokenLanguage> SpokenLanguages { get; set; }
}
public class CustomCreationConverter<T, TSerialized> : CustomCreationConverter<T> where TSerialized : T, new()
{
public override T Create(Type objectType)
{
return new TSerialized();
}
}
Example fiddle showing both options.
I have previously created methods that take a property as input using the following syntax:
public void MyMethod<T>(Expression<Func<T>> property) { }
public int MyProperty { get; set; }
public void SomeOtherMethod() {
MyMethod(() => MyProperty);
}
That way I can, for example, cast the property to a MemberExpression and get the fully qualified name of the property.
However, is it possible to do the same in a class constructor, or have a property that stores another property, like how the property variable does in MyMethod()?
I have tried the following, but that didn't work because the type parameter is not specified in either cases, but I wouldn't even know which type to use... especially because the type is magically inferred in the topmost example.
In constructor
public class MyClass<T> {
public MyClass(Expression<Func<T>> property) { }
}
public int MyProperty { get; set; }
var myClass = new MyClass(() => MyProperty);
As property
public class MyClass<T> {
public Expression<Func<T>> SomeProperty { get; set; }
}
public int MyProperty { get; set; }
var myClass = new MyClass() { SomeProperty = () => MyProperty };
So, how can I have a property expression in a constructor or property of its own without having the use the type definition?
Is it possible to [infer generic parameters] in a class constructor?
In C# generic inference is not supported on constructors, so you have to set it explicitly:
var myClass = new MyClass<int>(() => MyProperty);
The main reason it's not supported is because there are often many types that could be used as the generic constraint due to implicit type conversions, so the compiler would have to choose the "best" generic parameters based on the information that it has. The costs associated with designing, programming, testing, documenting, and integrating this logic outweigh the benefit, or at least don't provide a relative benefit that exceeds the other features that MS could spend time developing.
In this case you know the best generic parameter type because the MyProperty property returns an int. Technically you could use any type that is implicitly convertible from int (object, double, etc.) but int is the best fit.
Could I maybe use object as a catch-all?
Sure, but then your code isn't generic. You can just do
public class MyClass {
public MyClass(Expression<Func<object>> property) { }
}
You need to supply the type parameter of T
var myClass = new MyClass<int>(() => MyProperty);
var myClass = new MyClass<int>() { SomeProperty = () => MyProperty };
Otherwise it won't know what type of expression it should get in the constructor.
I'd like to create an instance of a class using unity where the class has two constructors with the same number of parameters.
Here is the instantiation:
_unityContainer.Resolve<IGradeType>(new ParameterOverride("gradeTypeStringFromXmlFile", gradeTypeStringFromXmlFile));
And here are the constructors:
public GradeType(string gradeTypeStringFromXmlFile)
{
_gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile;
}
public GradeType(Enum.GradeType gradeType)
{
_gradeType = gradeType;
}
If I try to do this I get an exception saying The type GradeType has multiple constructors of length 1. Unable to disambiguate.
I can set the attribute [InjectionConstructor] over one constructor to make it work with one, but then I can't create an instance with unity using the other constructor.
Is it some way to have multiple constructors with equal number of parameters and still use unity to create the instances?
Yes it's possible to tell Unity which constructor should it use, but you can only do this when you register your type with InjectionConstructor. If you want to use both constructor it's even complicated because you have to name your registrations and use that name when resolving.
Sample built with Unity version 2.1.505:
var continer = new UnityContainer();
continer.RegisterType<IGradeType, GradeType>("stringConstructor",
new InjectionConstructor(typeof(string)));
continer.RegisterType<IGradeType, GradeType>("enumConstructor",
new InjectionConstructor(typeof(EnumGradeType)));
IGradeType stringGradeType = continer.Resolve<IGradeType>("stringContructor" ,
new DependencyOverride(typeof(string), "some string"));
IGradeType enumGradeType = continer.Resolve<IGradeType>("enumConstructor",
new DependencyOverride(typeof(EnumGradeType), EnumGradeType.Value));
An alternative option using Reflection and following the Strategy Pattern.
1) Create a base class for the constructors' arguments
public abstract class ConstructorArgs
{
}
2) Create a sequence of different concrete arguments classes:
public class StringArg : ConstructorArgs
{
public string _gradeTypeStringFromXmlFile { get; set; }
public StringArg (string gradeTypeStringFromXmlFile)
{
this._gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile ;
}
}
public class EnumArg : ConstructorArgs
{
public Enum.GradeType _gradeType { get; set; }
public EnumArg (Enum.GradeType gradeType)
{
this._gradeType = gradeType ;
}
}
3) Now in your GradeType class create the methods required for the Reflection. The ParseArguments scans the args for properties and for each one that it finds, it copies its value to the respective property of the GradeType using the SetProperty. Since it uses the property name for the matching, it is important to keep the same property name across both the GradeType and the concrete ConstructorArgs:
private void SetProperty(String propertyName, object value)
{
var property = this.GetType().GetProperty(propertyName);
if (property != null)
property.SetValue(this, value);
}
private void ParseArguments(ConstructorArgs args)
{
var properties = args.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
this.SetProperty(propertyInfo.Name,
args.GetType().GetProperty(propertyInfo.Name).GetValue(args));
}
}
4) In your GradeType class create the respective properties (mind that you must use exactly the same names and types that you used in the concrete ConstructorArgs but you can use any access modifiers you like)
public string _gradeTypeStringFromXmlFile { get; set; }
public Enum.GradeType _gradeType { get; set; }
5) Create a constructor for your GradeType class with a parameter of type ConstructorArgs:
public GradeType(ConstructorArgs args)
{
this.ParseArguments(args);
}
6) Now you can register the GradeType in Unity using a single constructor but you can pass in different types as arguments when resolving it:
_unityContainer.RegisterType<IGradeType, GradeType>(
new InjectionConstructor( typeof(ConstructorArgs) ));
var args1 = new StringArg(gradeTypeStringFromXmlFile); // string
IGradeType gradeType1 = _unityContainer.Resolve<IGradeType>(
new ResolverOverride[]{new ParameterOverride("args", args1)});
var args2 = new EnumArg(gradeType); // enum
IGradeType gradeType2 = _unityContainer.Resolve<IGradeType>(
new ResolverOverride[]{new ParameterOverride("args", args2)});
If you are planning to repeatedly resolve your type in an iteration that approach might not be ideal, since Reflection comes with a performance penalty.
Remove one constructor, and cast the string to the enum, or vice-versa, and then resolve using the container.