I have a data class with several members
public interface IEquipmentHolder
{
ITypedServiceProvider<IGrabberChannel> VideoChannels { get; }
ITypedServiceProvider<IMicrophone> Microphones { get; }
ITypedServiceProvider<ISpeaker> Speakers { get; }
...
}
and a function
void visitChilds<T>(ITypedServiceProvider<T> childsList) where T : INamedComponent
{
...
}
In some place of my code, I want call the function for each field of the data class. So i do:
visitChilds(equipment.VideoChannels);
visitChilds(equipment.Microphones);
...
But, probably I am going to add some new fields in the data class and don't want to forget to fix these place after that.
My question: is it possible to to call generic function for each data member of the class using reflection? if it is not, can we put compile time check for new fields in the c# code?
How often are you going to add more fields and 'forget' to update calling code?
How fast are you going to find out that you've forgotten to add a call to visitChilds()?
How about IEquipmentHolder has only one property ITypedServiceProvider<INamedComponent> Items { get; }?
Reflection is slower than direct calls (maybe you should emit IL?) and the additional code may be just not worth the 'improvement', especially if it will be easy to spot that new set it not visited.
Also consider, that adding a new set of INamedComponents requires a breaking change to IEquipmentHolder interface and all implementations.
You could use the Expression framework to generate a lambda that will call visitChilds for each property on the interface.
You'd need to use reflection to generate the expression, but this would be a one off hit. After that you'd have a dynamically compiled lambda you could call that would be a lot quicker.
I've done something similiar before whereby I convert an instance of an object into a Dictionary<string,object> where each key is the name of a property and the value is the value of the property. We generated a lambda for each type we wanted to convert, and it worked really well.
If ITypedServiceProvider is covariant, i.e., declared like this,
ITypedServiceProvider<out T>
{
...
}
then you could have
IEquipmentHolder
{
IEnumerable<ITypedServiceProvider<INamedComponent>>
NamedComponents { get; }
}
then your could do
void VisitChildren(IEquipmentHolder equipment)
{
foreach(var provider in equipment.NamedComponents)
{
provider.SomeMemberOfITypedServiceProvider();
}
}
Related
I'm currently modifying a Blazor library and the souce code of the current state is available on gitlab.
My situation is as follows:
I have a LineChartData object which is supposed to store multiple Datasets for LineCharts.
These Datasets intern have a List of Data. Instead of just working with List<object> I wanted to be able to have List<TData>.
Because there is a Mixed Chart which can accept both LineChartDatasets and BarChartDatasets, there is an interface called IMixableDataset.
I started by making this interface generic so it now looks like this (simplified):
public interface IMixableDataset<TData>
{
List<TData> Data { get; }
}
I then made my implementing class (LineChartDataset) generic as well and it now looks like this (simplified):
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public List<TData> Data { get; }
}
Next up was LineChartData. I first made this generic as well and continued with that until I reached the top level (see current state of my master branch). However I later wanted to change this because I wanted to support multiple Datasets with different kind of values. For this reason I reverted the generic stuff in all classes "above" the Datasets and the LineChartData now looks like this (simplified):
public class LineChartData
{
// HashSet to avoid duplicates
public HashSet<LineChartDataset<object>> Datasets { get; }
}
I decided to go with LineChartDataset<object> because: Since everything is castable to object, (in my mind) XYZ<Whatever> should also be castable to XYZ<object> but as I learned, this is not the case.
The where keyword didn't help either since I don't want to enforce TData to have relations apart from object - it could be int, string or something completely different. The only relation these LineDatasets are supposed to have is that they are LineDatasets, not what type they contain.
I then learned about Covariance and Contravariance (out and in-keyword). I tried out to make TData in IMixableDataset covariant but since List and IList/ICollection are all invariant I was unable to persue.
I also read about IReadOnlyCollection<> which is covariant but I cannot use this because I have to be able to modify the list after creation.
I have also tried using implicit/explicit operators to convert LineChartDataset<whatever> to LineChartDataset<object> but this has a few issues:
Since I created a new instance, I would need to store and use the new instance instead of the original one to add items, completely destroying the typesafety I had with the original one.
Since there are many more properties in LineChartDataset I would have to clone all of them as well.
If there is a way to convert a more specific one to the other while preserving the instance and not having to write code for every property this might be a solution.
Complete sample which reproduces the error I get and shows the issue:
// Provides access to some Data of a certain Type for multiple Charts
public interface IMixableDataset<TData>
{
List<TData> Data { get; }
}
// Contains Data of a certain Type (and more) for a Line-Chart
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public List<TData> Data { get; } = new List<TData>();
}
// Contains Datasets (and more) for a Line-Chart
// This class should not be generic since I don't want to restrict what values the Datasets have.
// I only want to ensure that each Dataset intern only has one type of data.
public class LineChartData
{
// HashSet to avoid duplicates and Public because it has to be serialized by JSON.Net
public HashSet<LineChartDataset<object>> Datasets { get; } = new HashSet<LineChartDataset<object>>();
}
// Contains the ChartData (with all the Datasets) and more
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
LineChartDataset<int> intDataset = new LineChartDataset<int>();
intDataset.Data.AddRange(new[] { 1, 2, 3, 4, 5 });
config.ChartData.Datasets.Add(intDataset);
// the above line yields following compiler error:
// cannot convert from 'Demo.LineChartDataset<int>' to 'Demo.LineChartDataset<object>'
// the config will then get serialized to json and used to invoke some javascript
}
public void WorkingButBadUseCase()
{
LineChartConfig config = new LineChartConfig();
LineChartDataset<object> intDataset = new LineChartDataset<object>();
// this allows mixed data which is exactly what I'm trying to prevent
intDataset.Data.AddRange(new object[] { 1, 2.9, 3, 4, 5, "oops there's a string" });
config.ChartData.Datasets.Add(intDataset); // <-- No compiler error
// the config will then get serialized to json and used to invoke some javascript
}
}
The reason everything only has getters is because of my initial attempt with using out. Even thought this didn't work out, I learned that you usually don't expose Setters for Collection-properties. This is not fix and also not very important for the question but I think worth mentioning.
Second complete example. Here I'm using out and an IReadOnlyCollection. I have removed the descriptions of the class (already visible in the previous example) to make it shorter.
public interface IMixableDataset<out TData>
{
IReadOnlyCollection<TData> Data { get; }
}
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public IReadOnlyCollection<TData> Data { get; } = new List<TData>();
}
public class LineChartData
{
public HashSet<IMixableDataset<object>> Datasets { get; } = new HashSet<IMixableDataset<object>>();
}
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
IMixableDataset<int> intDataset = new LineChartDataset<int>();
// since it's ReadOnly, I of course can't add anything so this yields a compiler error.
// For my use case, I do need to be able to add items to the list.
intDataset.Data.AddRange(new[] { 1, 2, 3, 4, 5 });
config.ChartData.Datasets.Add(intDataset);
// the above line yields following compiler error (which fairly surprised me because I thought I correctly used out):
// cannot convert from 'Demo.IMixableDataset<int>' to 'Demo.IMixableDataset<object>'
}
}
So the question:
Is there anyway to have a mutable and covariant collection?
If not, is there a workaround or something I can do to achieve this functionality?
Additional stuff:
I'm using the newest version of everything (.net core, VS, blazor, C#). Since the library is .NET Standard I'm still on C# 7.3 there.
In the repo under WebCore/Pages/FetchData you can perfectly see what I want to achieve (see comments at the end of the file).
Looking more closely at your example, I see one major problem: you are attempting to involve value types (e.g. int) in type variance. For better or worse, C# type variance applies only to reference types.
So, no…sorry, but it is quite impossible to do exactly what you're asking. You would have to represent all value-type based collections as object, not as their specific value types.
Now, as far as reference-type collections go, your example will work fine, with one minor change. Here's a modified version of your second example showing it working, with that one minor change:
public interface IMixableDataset<out TData>
{
IReadOnlyCollection<TData> Data { get; }
}
public class LineChartDataset<TData> : IMixableDataset<TData>
{
private readonly List<TData> _list = new List<TData>();
public IReadOnlyCollection<TData> Data => _list;
public void AddRange(IEnumerable<TData> collection) => _list.AddRange(collection);
}
public class LineChartData
{
public HashSet<IMixableDataset<object>> Datasets { get; } = new HashSet<IMixableDataset<object>>();
}
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
// Must use reference types to take advantage of type variance in C#
LineChartDataset<string> intDataset = new LineChartDataset<string>();
// Using the non-interface method to add the range, you can still mutate the object
intDataset.AddRange(new[] { "1", "2", "3", "4", "5" });
// Your original code works fine when reference types are used
config.ChartData.Datasets.Add(intDataset);
}
}
In particular, note that I've added an AddRange() method to your LineChartDataset<TData> class. This provides a type-safe way to mutate the collection. Note that the code that wants to mutate the collection must know the correct type, bypassing the variance restrictions.
The variant interface IMixableDataset<TData> itself cannot, of course, include a way to add things, because this would not be type-safe. You would be able to treat your LineChartDataset<string> as a IMixableDataset<object>, and then if you could add things via that interface, you'd be able to add some other type of object, even a non-reference type like a boxed int value, to your collection that's supposed to only contain string objects.
But, just as the invariant List<T> can implement the covariant IReadOnlyCollection<T>, your concrete LineChartDataset<TData> class can implement IMixableDataset<TData> while still providing a mechanism for adding items. This works because while the concrete type determines what the object can actually do, the interfaces simply define a contract that users of the reference must abide by, allowing the compiler to ensure type safety where the interface is used, even when used in a variant way. (The invariant concrete type ensures type safety as well, but only because the type has to match exactly, which is of course more restrictive/less flexible.)
If you don't mind using object in place of any specific value type for the value-type-based collections, then the above would work. It's a bit clumsy, since any time you actually want to get the value type values out, you'd need to retrieve them as object and then cast as necessary to actually use them. But at least the broader variant approach would then succeed, and no special handling would be required for any reference types.
Aside: that type variance in C# is restricted to reference types is based on the pragmatic requirement that type variance doesn't affect the runtime code. It's just a compile-time type-conversion. This means that you have to be able to just copy references around. To support value types would require adding new boxing and unboxing logic where it otherwise wouldn't exist. It's also not quite as useful, because value types don't have the same rich degree of type inheritance that reference types can have (value types can only ever inherit object, so variant scenarios are much less useful and interesting, in general).
For the purposes of this question, a 'constant reference' is a reference to an object from which you cannot call methods that modify the object or modify it's properties.
I want something like this:
Const<User> user = provider.GetUser(); // Gets a constant reference to an "User" object
var name = user.GetName(); // Ok. Doesn't modify the object
user.SetName("New value"); // <- Error. Shouldn't be able to modify the object
Ideally, I would mark with a custom attribute (e.g. [Constant]) every method of a class that doesn't modify the instance, and only those methods can be called from the constant reference. Calls to other methods would result in an error, if possible, during compile time.
The idea is I can return a read-only reference to and be sure that it will not be modified by the client.
The technique you're referring to is called "const-correctness" which is a language feature of C++ and Swift, but not C#, unfortunately - however you're onto something by using a custom attribute because that way you can enforce it via a Roslyn extension - but that's a rabbit-hole.
Alternatively, there's a much simpler solution using interfaces: because C# (and I think the CLR too) does not support const-correctness (the closest we have is the readonly field modifier) the .NET base-class-library designers added "read-only interfaces" to common mutable types to allow a object (wheather mutable or immutable) to expose its functionality via an interface that only exposes immutable operations. Some examples include IReadOnlyList<T>, IReadOnlyCollection<T>, IReadOnlyDictionary<T> - while these are all enumerable types the technique is good for singular objects too.
This design has the advantage of working in any language that supports interfaces but not const-correctness.
For each type (class, struct, etc) in your project that needs to expose data without risk of being changed - or any immutable operations then create an immutable interface.
Modify your consuming code to use these interfaces instead of the concrete type.
Like so:
Supposing we have a mutable class User and a consuming service:
public class User
{
public String UserName { get; set; }
public Byte[] PasswordHash { get; set; }
public Byte[] PasswordSalt { get; set; }
public Boolean ValidatePassword(String inputPassword)
{
Hash[] inputHash = Crypto.GetHash( inputPassword, this.PasswordSalt );
return Crypto.CompareHashes( this.PasswordHash, inputHash );
}
public void ResetSalt()
{
this.PasswordSalt = Crypto.GetRandomBytes( 16 );
}
}
public static void DoReadOnlyStuffWithUser( User user )
{
...
}
public static void WriteStuffToUser( User user )
{
...
}
Then make an immutable interface:
public interface IReadOnlyUser
{
// Note that the interfaces' properties lack setters.
String UserName { get; }
IReadOnlyList<Byte> PasswordHash { get; }
IReadOnlyList<Byte> PasswordSalt { get; }
// ValidatePassword does not mutate state so it's exposed
Boolean ValidatePassword(String inputPassword);
// But ResetSalt is not exposed because it mutates instance state
}
Then modify your User class and consumers:
public class User : IReadOnlyUser
{
// (same as before, except need to expose IReadOnlyList<Byte> versions of array properties:
IReadOnlyList<Byte> IReadOnlyUser.PasswordHash => this.PasswordHash;
IReadOnlyList<Byte> IReadOnlyUser.PasswordSalt => this.PasswordSalt;
}
public static void DoReadOnlyStuffWithUser( IReadOnlyUser user )
{
...
}
// This method still uses `User` instead of `IReadOnlyUser` because it mutates the instance.
public static void WriteStuffToUser( User user )
{
...
}
So, these are the first two ideas I initially had, but don't quite solve the problem.
Using Dynamic Objects:
The first idea I had was creating a Dynamic Object that would intercept all member invokations and throw an error if the method being called isn't marked with a [Constant] custom attribute. This approach is problematic because a) We don't have the support of the compiler to check for errors in the code (i.e. method name typos) when dealing with dynamic objects, which might lead to a lot of runtime errors; and b) I intend to use this a lot, and searching for method names by name every time a method is called might have considerable performance impact.
Using RealProxy:
My second idea was using a RealProxy to wrap the real object and validate the methods being called, but this only works with objects that inherit from MarshalByRefObject.
I am designing a 3 layer framework
I would like to know if It's possible to pass attribiutes of an object to a function without declaring them explicitly ?
For example If I want to pass Id,Name to personnelBL.ValidateInsert(...)
I don't want the ValidateInsert function interface look like this : ValidateInsert(Id,Name)
The reason for that is that I want to write a base abstract class to contain a ValidateInsert(...)
abstract function so I will Inherit from that class in my BL Layer classes and If the ValidateInsert input parameters could be declared in a way that I could pass an object attribiutes in a general form It would really be nice .
Note: Someone might say that I can pass an object to the function using generics but I really don't want to pass an object ! I want to pass any object's attribiutes so I can Inherit that abstract base class in any entityBL classes .
I really could not explain what I want better ! Sorry for that and thanks for understanding me .
not sure that I fully understand what you want , but I think the below can help
You can use reflection.You can avoid the performance issues, is you create method per class on the fly and compile it (can use compile expression tree). and add your own attribute that you put only on relevant attributes.
Create an Interface, It can return dictionary of column name and their values. your abstract class will implement this interface.
hope this answer your question
I am not sure if i understand your question correctly, but are you looking for something similar to this-
public class Base<T, TFiled>
{
public void ValidateInsert(TFiled filed)
{
}
}
public class Derived : Base<Derived, long>
{
public long Id { get; set; }
}
public class AnotherDerived : Base<Derived, string>
{
public string IdSring { get; set; }
}
public class MyObject
{
private Derived d = new Derived();
private AnotherDerived anotherIsntance = new AnotherDerived();
public MyObject()
{
d.ValidateInsert(10);
anotherIsntance.ValidateInsert("some string");
}
}
Well, not really.
But you can get very close to!
You can use the Expression API. It's awesome. The code I'll post here is just pseudocode but you'll get the idea. I'll not worry about syntax but I'll try the hardest I can.
public static bool ValidateInsert(params Expression<Func<object,object>>[] properties)
{
//Here you'll do some code to get every property. You can do a foreach loop.
//I think you will need to use reflection to get the property values
} //Change Func<Object,Object> accordingly. This represents a function that takes an object and returns another object.
This is how you can achieve the syntax, but I'm not sure about functionality.
You'll need an "instance" object where you'll get the properties values from.
So, you could call it like this:
ValidadeInsert(x => x.Id, x => x.Name, x => x.Whatever)
Here you can see how to get the Getter method of a property. I think you can get the PropertyInfo from the lambda expression, but I'm not sure. You'll have to do some research and adapt it to your code, if you decide to follow this way.
Sorry about my english, but I think you understood what I meant.
Intro
I'm working with the legacy code which contains two classes:
I have a class which stores its value of System.Object type.
(I named this class as DomainItem)
Its Identifier property refers to
enum which holds information what a type of DomainItem is (in the
context of business domain).
There is also a class which stores these
items as an Enumerable List. (DomainItems)
What's more:
I don't want to change these classes into generic. This code is very sensitive and not covered by tests.
In order to get DomainItem, I must get it from DomainItems.Items collection.
Code
The code for classes is equivalent as below:
public class DomainItem
{
public Identifier Identifier { get; set; } // Readonly in the "real" code
public object Value { get; set; }
}
public class DomainItems
{
public IEnumerable<DomainItem> Items { get; set; }
}
The question is
How can I extend these classes using generics, to resolve type of Value property in the compile time. Is it even possible?
Example case might be as following:
DomainItem price = new DomainItem { Value = 25.20d, Identifier = Identifier.Price };
// ....
double priceValue = price.ProperValue; // generic property of type T
Obviously, above code is conceptual and it shows what I want to achieve. Any suggestions how to resolve that? Is it even possible?
Edit
My idea is to create a new IEnumerable<DomainItem<T>> where the collection is populated from non-generic DomainItem objects. Since the type of DomainItem.Value is known, it should be possible to make such collection somehow.
There's no such thing as a generic property, but you could easily create a generic method:
public T GetValue<T>() { ... }
public void SetValue<T>(T value) { ... }
You could then check typeof(T) within the method to make sure that it was appropriate for your identifier, ideally having made the identifier read-only. (It would be better as a constructor argument - I wouldn't expect it to make any sense to have a domain item whose identifier changed over time.)
Alternatively, you could just make the type of the Value property dynamic instead of object, assuming you're using C# 4+ with .NET 4+. Then your example code would compile - but it would perform an implicit (dynamic) conversion to double at execution time. You wouldn't get much safety there, but it would compile...
I am not sure how to implement what I have in mind using C# .Net 3.5. I have a static class called Common which contains common methods. One of the method is PrepareReportParameters. This method accepts a string ReportParams and parse it to get the parameter values. I load this ReportParams string into a Dictionary . And then verify whether the required elements exist. I check that like:
if (ReportParamList.ContainsKey("PAccount"))
{
ReportParamList.TryGetValue("PAccount", out PrimaryAccount);
}
where PrimaryAccount is a static variable in my Common class. And I can access this elsewhere as Common.PrimaryAccount.
Though, this approcah of accessing the report parameters will work but I want PrimaryAccount to be accessed as Common.ReportParameters.PrimaryAccount.
Here is the problem, I don't know what type ReportParameters should be and how can I have all the report parameters added to this type? How should I define ReportParameters? Does it sound feasible or it doesn't make any sense. Please H E L P!
It sounds like you're basically used to using global variables to pass around state. That's generally a really bad idea.
Why doesn't your method just return the primary account value? That can then be passed to other things which need it.
If you find yourself with a lot of static members - and in particular if other classes are fetching mutable static variables - consider whether there's a more OO design you could apply. It'll be easier to understand, easier to test, and easier to maintain.
EDIT: Okay, so currently you have:
public static class Common
{
public static int PrimaryAccount;
// other static fields
public static void PrepareReportParameters(string reportParameters)
{
// Code to set the fields
}
}
Instead of that, use a normal class:
public class ReportParameters
{
public int PrimaryAccount { get; private set; }
// Other properties
private ReportParameters(int primaryAccount, ....)
{
this.PrimaryAccount = primaryAccount;
}
// Could use a constructor instead, but I prefer methods when they're going to
// do work
public static ReportParameters Parse(string report)
{
// Parse the parameter, save values into local variables, then
return new ReportParameters(primaryAccount, ...);
}
}
Then call this from the rest of your code, and pass the ReportParameters reference to anything that needs it.
You could create a class called ReportParameters with the relevant strongly-typed properties, and give Common a static instance of it?
I'm not sure this is the best design. Theres a certain amount of 'code smell' to having Common.PrimaryAccount only to be allowed to be accessed after PrepareReportParameters is called. Maybe you'd consider an instance class, passing in the parameters in the constructor?