c# inheritance and ISerialize interface - c#

I am trying to binary format a list of Animals(either a cat or a dog). This goes well to some extend. Ive made use of the ISerialize interface in my base class, Animal. The two derived classes, dog and cat, however both have 1 extra property the base class doesn't have. Now, the serialization and deserialization of the base class animal gives me no problems. The problem starts when i try to serialize and deserialize either one of those extra properties from the derived classes I mentioned earlier. I feel like there is an easy fix to this but I am overseeing something.
Below I will post code of the following locations:
The ISerialize interface in the Animal class
The ISerialize interface in the Dog class
The ISerialize interface in the Cat class
The implementation of the serializer and deserializer in the Administration class (both are bound to two buttons in a form, so i can execute them)
The error Visual Studio throws at me (translation of the dutch text: "Member BadHabits is not found")
I thank you in advance for any remarks and/or solutions to this problem.
[Serializable()]
abstract public class Animal : ISellable, IComparable<Animal>, ISerializable
{
/// <summary>
/// The chipnumber of the animal. Must be unique. Must be zero or greater than zero.
/// </summary>
public int ChipRegistrationNumber { get; private set; }
public abstract decimal Price { get; }
/// <summary>
/// Date of birth of the animal.
/// </summary>
public SimpleDate DateOfBirth { get; private set; }
/// <summary>
/// The name of the animal.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Is the animal reserved by a future owner yes or no.
/// </summary>
public bool IsReserved { get; set; }
/// <summary>
/// Creates an animal.
/// </summary>
/// <param name="chipRegistrationNumber">The chipnumber of the animal.
/// Must be unique. Must be zero or greater than zero.</param>
/// <param name="dateOfBirth">The date of birth of the animal.</param>
/// <param name="name">The name of the animal.</param>
public Animal(int chipRegistrationNumber, SimpleDate dateOfBirth, string name)
{
if (chipRegistrationNumber < 0)
{
throw new ArgumentOutOfRangeException("Chipnummer moet groter of gelijk zijn aan 0");
}
ChipRegistrationNumber = chipRegistrationNumber;
DateOfBirth = dateOfBirth;
if (name == null)
{
throw new ArgumentNullException("Naam is null");
}
Name = name;
IsReserved = false;
}
/// <summary>
/// Retrieve information about this animal
///
/// Note: Every class inherits (automagically) from the 'Object' class,
/// which contains the virtual ToString() method which you can override.
/// </summary>
/// <returns>A string containing the information of this animal.
/// The format of the returned string is
/// "<ChipRegistrationNumber>, <DateOfBirth>, <Name>, <IsReserved>"
/// Where: IsReserved will be "reserved" if reserved or "not reserved" otherwise.
/// </returns>
public override string ToString()
{
string IsReservedString;
if (IsReserved)
{
IsReservedString = "reserved";
}
else
{
IsReservedString = "not reserved";
}
string info = ChipRegistrationNumber
+ ", " + DateOfBirth
+ ", " + Name
+ ", " + IsReservedString;
return info;
}
private Animal(SerializationInfo info, StreamingContext context)
{
ChipRegistrationNumber = (Int32)info.GetValue("Chipnumber", typeof(Int32));
DateOfBirth = (SimpleDate)info.GetValue("Date of Birth", typeof(SimpleDate));
Name = (String)info.GetValue("Name", typeof(String));
IsReserved = (bool)info.GetValue("Isreserved", typeof(bool));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Chipnumber", ChipRegistrationNumber);
info.AddValue("Date of Birth", DateOfBirth);
info.AddValue("Name", Name);
info.AddValue("Isreserved", IsReserved);
}
public int CompareTo(Animal other)
{
throw new NotImplementedException();
}
}
dog
[Serializable]
public class Dog : Animal
{
/// <summary>
/// The date of the last walk of the dog. Contains null if unknown.
/// </summary>
public SimpleDate LastWalkDate { get; set; }
/// <summary>
/// Creates a dog.
/// </summary>
/// <param name="chipRegistrationNumber">The chipnumber of the animal.
/// Must be unique. Must be zero or greater than zero.</param>
/// <param name="dateOfBirth">The date of birth of the animal.</param>
/// <param name="name">The name of the animal.</param>
/// <param name="lastWalkDate">The date of the last walk with the dog or null if unknown.</param>
public Dog(int chipRegistrationNumber, SimpleDate dateOfBirth,
string name, SimpleDate lastWalkDate) : base(chipRegistrationNumber, dateOfBirth, name)
{
if (lastWalkDate != null)
{
this.LastWalkDate = lastWalkDate;
}
}
/// <summary>
/// Retrieve information about this dog
///
/// Note: Every class inherits (automagically) from the 'Object' class,
/// which contains the virtual ToString() method which you can override.
/// </summary>
/// <returns>A string containing the information of this animal.
/// The format of the returned string is
/// "Dog: <ChipRegistrationNumber>, <DateOfBirth>, <Name>, <IsReserved>, <LastWalkDate>"
/// Where: IsReserved will be "reserved" if reserved or "not reserved" otherwise.
/// LastWalkDate will be "unknown" if unknown or the date of the last doggywalk otherwise.
/// </returns>
public override string ToString()
{
// TODO: Put your own code here to make the method return the string specified in the
// method description.
if (LastWalkDate == null)
{
return $"Dog: {base.ToString()} - unknown";
}
return $"Dog: {base.ToString()}, {LastWalkDate}";
}
public override decimal Price
{
get
{
if(ChipRegistrationNumber < 50000)
{
return 200;
}
return 350;
}
}
public new void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Lastwalk", LastWalkDate);
}
public Dog(SerializationInfo info, StreamingContext context) : base(info, context)
{
LastWalkDate = (SimpleDate)info.GetValue("Lastwalk", typeof(SimpleDate));
}
}
cat
[Serializable]
public class Cat : Animal
{
/// <summary>
/// Description of the bad habits that the cat has (e.g. "Scratches the couch").
/// or null if the cat has no bad habits.
/// </summary>
public string BadHabits { get; set; }
/// <summary>
/// Creates a cat.
/// </summary>
/// <param name="chipRegistrationNumber">The chipnumber of the animal.
/// Must be unique. Must be zero or greater than zero.</param>
/// <param name="dateOfBirth">The date of birth of the animal.</param>
/// <param name="name">The name of the animal.</param>
/// <param name="badHabits">The bad habbits of the cat (e.g. "scratches the couch")
/// or null if none.</param>
public Cat(int chipRegistrationNumber, SimpleDate dateOfBirth,
string name, string badHabits) : base(chipRegistrationNumber, dateOfBirth, name)
{
this.BadHabits = badHabits;
}
/// <summary>
/// Retrieve information about this cat
///
/// Note: Every class inherits (automagically) from the 'Object' class,
/// which contains the virtual ToString() method which you can override.
/// </summary>
/// <returns>A string containing the information of this animal.
/// The format of the returned string is
/// "Cat: <ChipRegistrationNumber>, <DateOfBirth>, <Name>, <IsReserved>, <BadHabits>"
/// Where: IsReserved will be "reserved" if reserved or "not reserved" otherwise.
/// BadHabits will be "none" if the cat has no bad habits, or the bad habits string otherwise.
/// </returns>
public override string ToString()
{
// TODO: Put your own code here to make the method return the string specified in the
// method description.
if (String.IsNullOrEmpty(BadHabits))
{
BadHabits = "none";
}
return $"{"Cat: "}{base.ToString()}, {BadHabits}";
}
public override decimal Price
{
get
{
int korting = BadHabits.Length - 60;
if ( korting > 20)
{
return korting;
}
return 20;
}
}
public new void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("BadHabits", this.BadHabits);
}
public Cat(SerializationInfo info, StreamingContext context) : base(info, context)
{
this.BadHabits = (String)info.GetString("BadHabits");
}
}
Administration
public void Save(String fileName)
{
Stream fileStream = File.Open(fileName, FileMode.Create);
BinaryFormatter format = new BinaryFormatter();
format.Serialize(fileStream, Animals);
fileStream.Close();
}
public void Load(String fileName)
{
FileStream fileStream;
BinaryFormatter format = new BinaryFormatter();
fileStream = File.OpenRead(fileName);
Animals = (List<Animal>)format.Deserialize(fileStream);
fileStream.Close();
}
Error message

The problem is the "new" part in your child method. By writing "new" on child class, implementation of inherited methods are going to be hidden.
Constructors are written properly, methods are the problem.
Here is one way you can fix this:
class Animal
{
...
public virtual GetObjectData(... params ...)
{
// populate properties
}
...
}
class Dog
{
public override GetObjectData(... params ...)
{
base.GetObjectData(... params ...)
// populate additional Dog properties
}
}

Related

Get the exact type as property in C# class

Sorry for the general title but it's a bit hard to explain in few words what is my problem currently.
So I have a simple class factory like this:
public Model Construct<T>(T param) where T : IModelable
{
new Model {Resource = param};
return n;
}
The Model class looks like this:
public class Model
{
public object Resource { get; set; }
}
The problem is, that you can see, is the Resource is currently an object. And I would like that Resource should be the type, what is get from the Construct and not lost the type-safe...
I tried to solve it with type parameter but it fails, because I can extend Model class with type parameter but what if I would like to store it to a simple class repository?
Then Construct will work, but if I would like to get the instanced class from the repository, I have to declare the type paramter again like:
Repository.Get<Model<Spaceship>>(0) .... and of course it's wrong because I would like that Model itself knows, what type of Resource has been added in Construct.
Does anybody any idea how to handle this?
The whole code currently look like this:
/// <summary>
/// Class Repository
/// </summary>
public sealed class Repository
{
/// <summary>
/// The _lock
/// </summary>
private static readonly object _lock = new object();
/// <summary>
/// The _syncroot
/// </summary>
private static readonly object _syncroot = new object();
/// <summary>
/// The _instance
/// </summary>
private static volatile Repository _instance;
/// <summary>
/// The _dict
/// </summary>
private static readonly Dictionary<int, object> _dict
= new Dictionary<int, object>();
/// <summary>
/// Prevents a default data of the <see cref="Repository" /> class from being created.
/// </summary>
private Repository()
{
}
/// <summary>
/// Gets the items.
/// </summary>
/// <value>The items.</value>
public static Repository Data
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null) _instance = new Repository();
}
}
return _instance;
}
}
/// <summary>
/// Allocates the specified id.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="parameter">The parameter.</param>
/// <resource name="id">The id.</resource>
/// <resource name="parameter">The parameter.</resource>
public void Allocate(int id, object parameter)
{
lock (_syncroot)
{
_dict.Add(id, parameter);
}
}
/// <summary>
/// Gets the specified id.
/// </summary>
/// <typeparam name="T">The type of the tref.</typeparam>
/// <param name="id">The id.</param>
/// <returns>``0.</returns>
/// <resource name="id">The id.</resource>
public T Get<T>(int id)
{
lock (_syncroot)
{
return (T) _dict[id];
}
}
}
/// <summary>
/// Class IModelFactory
/// </summary>
public sealed class ModelFactory
{
/// <summary>
/// The _lock
/// </summary>
private static readonly object _lock = new object();
/// <summary>
/// The _instance
/// </summary>
private static volatile ModelFactory _instance;
/// <summary>
/// Prevents a default instance of the <see cref="ModelFactory" /> class from being created.
/// </summary>
private ModelFactory()
{
}
/// <summary>
/// Gets the data.
/// </summary>
/// <value>The data.</value>
public static ModelFactory Data
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null) _instance = new ModelFactory();
}
}
return _instance;
}
}
/// <summary>
/// Constructs the specified param.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="param">The param.</param>
/// <returns>Model{``0}.</returns>
public Model Construct<T>(T param) where T : IModelable
{
var n = new Model {Resource = param};
return n;
}
}
/// <summary>
/// Class Model
/// </summary>
/// <typeparam name="T"></typeparam>
public class Model
{
public object Resource { get; set; }
}
/// <summary>
/// Interface IModelable
/// </summary>
public interface IModelable
{
/// <summary>
/// Gets or sets the mass.
/// </summary>
/// <value>The mass.</value>
float Mass { get; set; }
}
/// <summary>
/// Class spaceship
/// </summary>
public class Spaceship : IModelable
{
/// <summary>
/// Gets or sets the mass.
/// </summary>
/// <value>The mass.</value>
public float Mass { get; set; }
}
So the problem will be lighted here:
Add to the Repository:
Repository.Data.Allocate(1, ModelFactory.Data.Construct(new Spaceship()));
It's okay, but after:
var test_variable = Repository.Data.Get<Model>(1);
So now I have a non type-safe object from a type parameter, I don't know, that what type of class has been stored with the c model construction.
I'm very thankful for the suggestions of using type paramter on the Model class as well, but than it will come up another problem, because I have to change the Get function with it:
var test_variable = Repository.Data.Get<Model<Spaceship>>(1);
But that's definitely wrong, because I won't know, that what kind of type of class has been stored in the model..., so I would like to achieve to avoid this type parameter definition when I would like to load the instance from the Repository.
You can solve this by making your Model class generic, like this:
public class Model<T>
{
public T Resource { get; set; }
}
Then, your Construct method could work like this:
public Model<T> Construct<T>(T param) where T : IModelable<T>
{
return new Model<T>() {Resource = param};
}
You probably need a generic type in the model class:
public class Model<T>
{
public T Resource { get; set; }
}
This sort of structure is one approach you could take:
public Model<T> Construct<T>(T param) where T : IModelable
{
var n = new Model<T> {Resource = param};
return n;
}
public class Model<T> : IModel<T> where T : IModelable
{
public T Resource { get; set; }
}
public interface IModel<out T> where T : IModelable
{
T Resource { get; }
}
This covariant interface allows you to refer to the types more generically where you wish, in the same way that you can pass an IEnumerable<string> into something expecting an IEnumerable<object>.
IModel<Spaceship> shipModel = // whatever
IModel<IModelable> model = shipModel;
//or even:
List<Model<Spaceship>> shipModels = // whatever
IEnumerable<IModel<IModelable>> models = shipModels;

C# Immutable & Mutable types without duplication

Given the following implementation of mutable and immutable types, is there a way to avoid duplicate code (mainly the duplicate properties)?
I'd like to work with immutable type by default unless a mutable type is required (e.g. when binding to UI elements).
We're using .NET framework 4.0, but plan switching to 4.5 soon.
public class Person {
public string Name { get; private set; }
public List<string> Jobs { get; private set; } // Change to ReadOnlyList<T>
public Person() {}
public Person(Mutable m) {
Name = m.Name;
}
public class Mutable : INotifyPropertyChanged {
public string Name { get; set; }
public List<string> Jobs { get; set; }
public Mutable() {
Jobs = new List<string>();
}
public Mutable(Person p) {
Name = p.Name;
Jobs = new List<string>(p.Jobs);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
// TODO: implement
}
}
}
public class Consumer {
public Consumer() {
// We can use object initializers :)
Person.Mutable m = new Person.Mutable {
Name = "M. Utable"
};
// Consumers can happily mutate away....
m.Name = "M. Utated";
m.Jobs.Add("Herper");
m.Jobs.Add("Derper");
// But the core of our app only deals with "realio-trulio" immutable types.
// Yey! Have constructor with arity of one as opposed to
// new Person(firstName, lastName, email, address, im, phone)
Person im = new Person(m);
}
}
I made something that does what you ask recently (using T4 templates), so it is absolutely possible:
https://github.com/xaviergonz/T4Immutable
For example, given this:
[ImmutableClass(Options = ImmutableClassOptions.IncludeOperatorEquals)]
class Person {
private const int AgeDefaultValue = 18;
public string FirstName { get; }
public string LastName { get; }
public int Age { get; }
[ComputedProperty]
public string FullName {
get {
return FirstName + " " + LastName;
}
}
}
It will automatically generate for you in a separate partial class file the following:
A constructor such as public Person(string firstName, string
lastName, int age = 18) that will initialize the values.
Working implementations for Equals(object other) and Equals(Person other).
Also it will add the IEquatable interface for you. Working
implementations for operator== and operator!=
A working implementation of GetHashCode() A better ToString() with output such as "Person { FirstName=John, LastName=Doe, Age=21 }"
A Person With(...) method that can be used to generate a new immutable clone with 0 or more properties changed (e.g. var janeDoe = johnDoe.With(firstName: "Jane", age: 20)
So it will generate this (excluding some redundant attributes):
using System;
partial class Person : IEquatable<Person> {
public Person(string firstName, string lastName, int age = 18) {
this.FirstName = firstName;
this.LastName = lastName;
this.Age = age;
_ImmutableHashCode = new { this.FirstName, this.LastName, this.Age }.GetHashCode();
}
private bool ImmutableEquals(Person obj) {
if (ReferenceEquals(this, obj)) return true;
if (ReferenceEquals(obj, null)) return false;
return T4Immutable.Helpers.AreEqual(this.FirstName, obj.FirstName) && T4Immutable.Helpers.AreEqual(this.LastName, obj.LastName) && T4Immutable.Helpers.AreEqual(this.Age, obj.Age);
}
public override bool Equals(object obj) {
return ImmutableEquals(obj as Person);
}
public bool Equals(Person obj) {
return ImmutableEquals(obj);
}
public static bool operator ==(Person a, Person b) {
return T4Immutable.Helpers.AreEqual(a, b);
}
public static bool operator !=(Person a, Person b) {
return !T4Immutable.Helpers.AreEqual(a, b);
}
private readonly int _ImmutableHashCode;
private int ImmutableGetHashCode() {
return _ImmutableHashCode;
}
public override int GetHashCode() {
return ImmutableGetHashCode();
}
private string ImmutableToString() {
var sb = new System.Text.StringBuilder();
sb.Append(nameof(Person) + " { ");
var values = new string[] {
nameof(this.FirstName) + "=" + T4Immutable.Helpers.ToString(this.FirstName),
nameof(this.LastName) + "=" + T4Immutable.Helpers.ToString(this.LastName),
nameof(this.Age) + "=" + T4Immutable.Helpers.ToString(this.Age),
};
sb.Append(string.Join(", ", values) + " }");
return sb.ToString();
}
public override string ToString() {
return ImmutableToString();
}
private Person ImmutableWith(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return new Person(
!firstName.HasValue ? this.FirstName : firstName.Value,
!lastName.HasValue ? this.LastName : lastName.Value,
!age.HasValue ? this.Age : age.Value
);
}
public Person With(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return ImmutableWith(firstName, lastName, age);
}
}
And there are some more features as explained in the project page.
PS: If you want a property that's a list of other immutable objects just add:
public ImmutableList<string> Jobs { get; }
No, there's no easy way to avoid duplicate code.
What you've implemented is effectivly the builder pattern. The .NET StringBuilder class follows the same approach.
The support for immutable types in C# is a bit lacking, and could do with some language specific features to make it easier. Having to create a builder is a real pain, as you've discovred. An alternative is to have a constructor that takes all the values, but you tend to end up with the mother of all constructors, which makes the code unreadable.
Since the properties don't have the same visibility, this is not duplicate code. If their visibilty were the same, Person could inherit from Mutable to avoid duplication. Right now, I don't think there is code to factorize in what you show.
Think about using code generation to map each mutable to its immutable equivalent.
I personally like T4 code generation, aided by T4Toolbox library.
You can quite easily parse your code using EnvDTE.
You can find tons of high-quality information about T4 on Oleg Sych blog
http://www.olegsych.com/
Code generation could be hard to handle in the beginning, but it solves the infamous issue of the code-that-must-be-duplicated.
One for your consideration depending on if you're creating a public facing API is to consider 'popcicle immutability' as discussed by Eric Lippert. The nice thing about this is you don't need any duplication at all.
I've used something in reverse, my classes are mutable until a certain point when some calculations are going to occur at which point I call a Freeze() method. All changes to properties call a BeforeValueChanged() method, which if frozen throws an exception.
What you need is something whereby the classes are frozen by default, and you unfreeze them if you need them mutable. As others have mentioned if frozen you need to be returning read only copies of lists etc.
Here's an example of a little class I put together:
/// <summary>
/// Defines an object that has a modifiable (thawed) state and a read-only (frozen) state
/// </summary>
/// <remarks>
/// All derived classes should call <see cref="BeforeValueChanged"/> before modifying any state of the object. This
/// ensures that a frozen object is not modified unexpectedly.
/// </remarks>
/// <example>
/// This sample show how a derived class should always use the BeforeValueChanged method <see cref="BeforeValueChanged"/> method.
/// <code>
/// public class TestClass : Freezable
/// {
/// public String Name
/// {
/// get { return this.name; }
/// set
/// {
/// BeforeValueChanged();
/// this.name = name;
/// }
/// }
/// private string name;
/// }
/// </code>
/// </example>
[Serializable]
public class Freezable
{
#region Locals
/// <summary>Is the current instance frozen?</summary>
[NonSerialized]
private Boolean _isFrozen;
/// <summary>Can the current instance be thawed?</summary>
[NonSerialized]
private Boolean _canThaw = true;
/// <summary>Can the current instance be frozen?</summary>
[NonSerialized]
private Boolean _canFreeze = true;
#endregion
#region Properties
/// <summary>
/// Gets a value that indicates whether the object is currently modifiable.
/// </summary>
/// <value>
/// <c>true</c> if this instance is frozen; otherwise, <c>false</c>.
/// </value>
public Boolean IsFrozen
{
get { return this._isFrozen; }
private set { this._isFrozen = value; }
}
/// <summary>
/// Gets a value indicating whether this instance can be frozen.
/// </summary>
/// <value>
/// <c>true</c> if this instance can be frozen; otherwise, <c>false</c>.
/// </value>
public Boolean CanFreeze
{
get { return this._canFreeze; }
private set { this._canFreeze = value; }
}
/// <summary>
/// Gets a value indicating whether this instance can be thawed.
/// </summary>
/// <value>
/// <c>true</c> if this instance can be thawed; otherwise, <c>false</c>.
/// </value>
public Boolean CanThaw
{
get { return this._canThaw; }
private set { this._canThaw = value; }
}
#endregion
#region Methods
/// <summary>
/// Freeze the current instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance can not be frozen for any reason.</exception>
public void Freeze()
{
if (this.CanFreeze == false)
throw new InvalidOperationException("The instance can not be frozen at this time.");
this.IsFrozen = true;
}
/// <summary>
/// Does a Deep Freeze for the duration of an operation, preventing it being thawed while the operation is running.
/// </summary>
/// <param name="operation">The operation to run</param>
internal void DeepFreeze(Action operation)
{
try
{
this.DeepFreeze();
operation();
}
finally
{
this.DeepThaw();
}
}
/// <summary>
/// Applies a Deep Freeze of the current instance, preventing it be thawed, unless done deeply.
/// </summary>
internal void DeepFreeze()
{
// Prevent Light Thawing
this.CanThaw = false;
this.Freeze();
}
/// <summary>
/// Applies a Deep Thaw of the current instance, reverting a Deep Freeze.
/// </summary>
internal void DeepThaw()
{
// Enable Light Thawing
this.CanThaw = true;
this.Thaw();
}
/// <summary>
/// Thaws the current instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance can not be thawed for any reason.</exception>
public void Thaw()
{
if (this.CanThaw == false)
throw new InvalidOperationException("The instance can not be thawed at this time.");
this.IsFrozen = false;
}
/// <summary>
/// Ensures that the instance is not frozen, throwing an exception if modification is currently disallowed.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance is currently frozen and can not be modified.</exception>
protected void BeforeValueChanged()
{
if (this.IsFrozen)
throw new InvalidOperationException("Unable to modify a frozen object");
}
#endregion
}

Strange issue using PropertyInfo in C#

I have a class like so:
public class CompanyData
{
# region Properties
/// <summary>
/// string CompanyNumber
/// </summary>
private string strCompanyNumber;
/// <summary>
/// string CompanyName
/// </summary>
private string strCompanyName;
[Info("companynumber")]
public string CompanyNumber
{
get
{
return this.strCompanyNumber;
}
set
{
this.strCompanyNumber = value;
}
}
/// <summary>
/// Gets or sets CompanyName
/// </summary>
[Info("companyName")]
public string CompanyName
{
get
{
return this.strCompanyName;
}
set
{
this.strCompanyName = value;
}
}
/// <summary>
/// Initializes a new instance of the CompanyData class
/// </summary>
public CompanyData()
{
}
/// <summary>
/// Initializes a new instance of the CompanyData class
/// </summary>
/// <param name="other"> object company data</param>
public CompanyData(CompanyData other)
{
this.Init(other);
}
/// <summary>
/// sets the Company data attributes
/// </summary>
/// <param name="other">object company data</param>
protected void Init(CompanyData other)
{
this.CompanyNumber = other.CompanyNumber;
this.CompanyName = other.CompanyName;
}
/// <summary>
/// Getting array of entity properties
/// </summary>
/// <returns>An array of PropertyInformation</returns>
public PropertyInfo[] GetEntityProperties()
{
PropertyInfo[] thisPropertyInfo;
thisPropertyInfo = this.GetType().GetProperties();
return thisPropertyInfo;
}
}
A csv file is read and a collection of CompanyData objects is created
In this method I am trying to get properties and values:
private void GetPropertiesAndValues(List<CompanyData> resList)
{
foreach (CompanyData resRow in resList)
{
this.getProperties = resRow.ExternalSyncEntity.GetEntityProperties();
this.getValues = resRow.ExternalSyncEntity.GetEntityValue(resRow.ExternalSyncEntity);
}
}
Here's the problem, for the first object the GetEntityProperties() returns the CompanyNumber as the first element in the array. For the remaining objects it returns CompanyName as the first element.
Why is the sequence not consistent?
Regards.
The Type.GetProperties() does not return ordered result.
The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies.
If you want to use ordered/consistent result, it is better to sort the returned array.

Can I create a List<Class<T>>?

I have a class
public class Setting<T>
{
public string name { get; set; }
public T value { get; set; }
}
now I want to create an IList<Setting<T>> but with different types of Setting<T>'s T in it, I want e.G.
List<Setting<T>> settingsList;
settingsList.Add(new Setting<int>());
settingsList.Add(new Setting<string>());
I've tried IList<Setting<T>> but this seems not possible since the compiler doesn't find Type T.
I know that I could use object but I want it to be strongly typed. So my question is if there is a possibility of getting this working.
Generic types do not have a common type or interface amongst concrete definitions by default.
Have your Setting<T> class implement an interface (or derive from a common class) and create a list of that interface (or class).
public interface ISetting { }
public class Setting<T> : ISetting
{
// ...
}
// example usage:
IList<ISetting> list = new List<ISetting>
{
new Setting<int> { name = "foo", value = 2 },
new Setting<string> { name = "bar", value "baz" },
};
You have to use a common ancestor class for all the class types that you put into the list. If it should be arbitrary types you have to use object for T
You can use T only inside your class:
class Setting<T>
{
// T is defined here
}
not outside. Outside you need to specify the concrete type:
Settings<string> stringSettings = new Settings<string>();
or
List<Settings<string>> list = new List<Settings<string>>();
list.Add(stringSettings);
ye you can do it by using reflection
i wrote such code for another question but you can use this
public abstract class GenericAccess
{
public static IList<T> GetObjectListFromArray<T>(T item)
{
var r = new List<T>();
var obj = typeof(T).Assembly.CreateInstance(typeof(T).FullName);
var p = obj.GetType().GetProperty("Id", System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (p != null)
{
p.SetValue(obj, item, null);
var m = r.GetType().GetMethod("Add");
m.Invoke(r, new object[] { obj });
}
return r;
}
}
and call it like this
IList<int> r = GenericAccess.GetObjectListFromArray<int>(1);
/// <summary>
/// 消息回调委托
/// </summary>
/// <typeparam name="T">TClass</typeparam>
/// <param name="result">返回实体</param>
/// <param name="args">内容包</param>
public delegate void CallbackHandler<in T>(T result, BasicDeliverEventArgs args);
/// <summary>
/// 注册监听程序接口
/// </summary>
public interface IRegistration
{
/// <summary>
/// 路由名称
/// </summary>
string ExchangeName { get; }
/// <summary>
/// 路由Key
/// </summary>
string RouteKey { get; }
/// <summary>
/// 回调操作
/// </summary>
/// <param name="args"></param>
void Call(BasicDeliverEventArgs args);
}
/// <summary>
/// 注册监听程序
/// </summary>
/// <typeparam name="T"></typeparam>
public class Registration<T> : IRegistration where T : class
{
/// <summary>
/// 路由名称
/// </summary>
public string ExchangeName { get; set; }
/// <summary>
/// 路由Key
/// </summary>
public string RouteKey { get; set; }
/// <summary>
/// 消息处理器委托
/// </summary>
public CallbackHandler<T> Handler { get; set; }
/// <summary>
/// 接口回调操作
/// </summary>
/// <param name="args"></param>
public void Call(BasicDeliverEventArgs args)
{
var message = Encoding.UTF8.GetString(args.Body.ToArray());
Handler?.Invoke(JsonConvertHandler.DeserializeObject<T>(message), args);
}
}
/// <summary>
/// 消息监听处理器
/// </summary>
public static class ListenerHandler
{
/// <summary>
/// 单任务锁
/// </summary>
private static readonly object Locker = new object();
/// <summary>
/// 所有注册列表
/// </summary>
public static List<IRegistration> Registrations { get; private set; }
/// <summary>
/// 单例化
/// </summary>
static ListenerHandler()
{
List<ListenerSetting> listeners = MqNoticeHandler.Setting.GetListeners();
MqNoticeHandler.Listener(OnMessage, listeners);
}
/// <summary>
/// 注册监听
/// </summary>
/// <param name="registration"></param>
public static void Register(IRegistration registration)
{
lock (Locker)
Registrations.Add(registration);
}
/// <summary>
/// 解除注册
/// </summary>
/// <param name="registration"></param>
public static void UnRegister(IRegistration registration)
{
lock (Locker)
Registrations.Remove(registration);
}
/// <summary>
/// 获取监听列表
/// </summary>
/// <param name="exchangeName"></param>
/// <param name="routeKey"></param>
/// <returns></returns>
public static IEnumerable<IRegistration> GetRegistrations(string exchangeName, string routeKey)
{
return Registrations.Where(x => x.ExchangeName == exchangeName && x.RouteKey == routeKey);
}
/// <summary>
/// 消息回调处理
/// </summary>
/// <param name="consumer">消费者</param>
/// <param name="args">消息包</param>
/// <returns></returns>
private static MqAckResult OnMessage(EventingBasicConsumer consumer, BasicDeliverEventArgs args)
{
//Example Query and Call
Registrations.First().Call(args);
//Return
return new MqAckResult { Accept = true, ReQueue = false };
}
}

.NET 4.0 application to use Microsoft ADO Ext. 6.0 for DDL and Security

I try to compile Xsd2Db solution in .NET 4.0 framework. I add a reference to "Microsoft ADO Ext. 6.0 for DDL and Security" and use it in this way -
using System;
using System.IO;
using ADOX;
namespace Xsd2Db.Data
{
/// <summary>
/// Summary description for JetDataSchemaAdapter.
/// </summary>
public sealed class JetDataSchemaAdapter : AdoxDataSchemaAdapter
{
/// <summary>
///
/// </summary>
internal const string Extension = ".mdb";
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
internal static string GetPath(string name)
{
return Path.GetFullPath(
Path.ChangeExtension(name, Extension));
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
internal static string GetConnectionString(string name)
{
return String.Format(
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Jet OLEDB:Engine Type=5;",
GetPath(name));
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
protected override void DeleteCatalog(string name)
{
File.Delete(GetPath(name));
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
protected override void CreateCatalog(string name)
{
// Force the run-time to let go of the file! Otherwise,
// cleanup and other operations might fail because the file
// will still be in use.
Catalog catalog = new CatalogClass();
catalog.Create(GetConnectionString(name));
catalog.ActiveConnection = null;
catalog = null;
GC.Collect();
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
protected override Catalog OpenCatalog(string name)
{
Catalog catalog = new CatalogClass();
catalog.let_ActiveConnection(GetConnectionString(name));
return catalog;
}
}
}
Now I have an error during compiling -- "The type 'ADOX.CatalogClass' has no constructors defined".
Clicking the error link brings me to the meta data -
#region Assembly Interop.ADOX.dll, v4.0.30319
// C:\xsd\xsd2db\xsd2db\Common\DataSchemaAdapter\obj\Debug\Interop.ADOX.dll
#endregion
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace ADOX {
[Guid("00000602-0000-0010-8000-00AA006D2EA4")]
[ClassInterface(0)]
[TypeLibType(2)]
public class CatalogClass : _Catalog, Catalog {
public CatalogClass();
[DispId(1)]
public virtual dynamic ActiveConnection { get; set; }
[DispId(4)]
public virtual Groups Groups { get; }
[DispId(2)]
public virtual Procedures Procedures { get; }
[DispId(0)]
public virtual Tables Tables { get; }
[DispId(5)]
public virtual Users Users { get; }
[DispId(3)]
public virtual Views Views { get; }
[DispId(6)]
public virtual dynamic Create(string ConnectString);
[DispId(7)]
public virtual string GetObjectOwner(string ObjectName, ObjectTypeEnum ObjectType, object ObjectTypeId = Type.Missing);
[DispId(1)]
public virtual void let_ActiveConnection(object pVal);
[DispId(8)]
public virtual void SetObjectOwner(string ObjectName, ObjectTypeEnum ObjectType, string UserName, object ObjectTypeId = Type.Missing);
}
}
We can see public CatalogClass(); but not a constructor.
I believe in .NET 1.x this worked. But why .NET 4.0 has this problem? Anyone encoutered this and What is the best way to handle this?
Thanks!
I did a search and found out the problem does exist in .NET 4.0. Here are some explanations and solutions -
http://blogs.msdn.com/b/mshneer/archive/2009/12/07/interop-type-xxx-cannot-be-embedded-use-the-applicable-interface-instead.aspx;
http://mokosh.co.uk/post/2010/04/10/net-4-0-interop-type-cannot-be-embedded-use-the-applicable-interface-instead/

Categories