In some legacy code, there are hundreds of occurrences of the following code snippets:
myObj.ReportGenerator.Preview = reportingObj.PreviewDocument;
... whereas both the "ReportGenerator" and the "ReportingObj" are instances of a third party library and therefore not modifyable.
This code did work well under Windows XP, but running the program in Windows 7 does require the following additional line of code:
reportingObj.Render();
myObj.ReportGenerator.Preview = reportingObj.PreviewDocument;
Unfortunately, there are hundreds of occurences of this piece of code all of the code base, and manually searching for them sounds like quite error-prone a process.
As "ReportGenerator" and "reportingObj" are third party, I cannot change their getter / setter.
What are elegant ways of approaching such an issue?
You could wrap ReportingObj in a class of your own in which you just delegate to the original ReportingObj, but for the PreviewDocument property check to see if Render() was called and if not call it - something like this:
public Foo PreviewDocument
{
get
{
if (!_rendered)
{
_originalreportingObj.Render();
_rendered = true;
}
return _originalreportingObj.PreviewDocument;
}
}
You could change the class of myObj, which I assume is under your control, and have the ReportGenerator property return a wrapper class that either calls the original setter of the Preview or calls it after calling Render():
public class ReportGeneratorWrapper
{
private ReportGenerator m_InnerReportGenerator;
public PreviewDocument Preview
{
get
{
return m_InnerReportGenerator;
}
set
{
if (IsNT6OrAbove)
value.Render();
m_InnerReportGenerator = value;
}
}
}
You might find that the least amount of rework will be to create a static class, something like:
public class Previewer
{
public static PreviewDocumentType PreviewDocument(ReportingObject reportingObj) {
reportingObj.Render();
return reportingObj.PreviewDocument;
}
}
where PreviewDocumentType is the type returned from PreviewDocument and ReportingObject is the type of reporting object.
You can then replace
reportingObj.PreviewDocument;
with
Previewer.PreviewDocument(reportingObj);
Related
Context
So after researching a bit about UI toolkit, i decided to use it for my project as i am very used to CSS and i despise the "default/legacy" UI system Unity3D offers.
Unfortunately its on a very early stage of development and they seem to be taking it in a "editor GUI" direction. Regardless i choose it as it made designing the UI, way faster than i could ever do with the usual "Unity3D UI" method of doing things.
Objective
My objective is relatively simple, i want to create a behavior that updates the UI based on some variable while hiding/abstracting this behavior(in other words i want to bind the value of the UI to some integer on a data class).
In the future this will be further complicated due to me slowly migrating everything to a multiplayer context
Main Problem
After hours of searching/reading the documentation, and brute forcing my way into the problem, i managed to reach to a simple solution, which i will abreviate due to me hardcoding some very similar behaviour:
GameObject trackCanvas = GameObject.Find("Canvas");
UIDocument docum= trackCanvas.GetComponent<UIDocument>();
Label foodUI = docum.rootVisualElement.Q<Label>(name: "FoodVar");
if(playerResources!= null){
SerializedObject pR = new SerializedObject(playerResources);
SerializedProperty foodProp = pR.FindProperty("food");
foodUI.BindProperty(foodProp);
}else{
foodUI.Unbind();
}
Pretty simple solution and better yet i saved some thinking time. It all worked like a charm until i try to build it and... I see multiple errors relating to importing UnityEditor which i started removing it(since i pretty much import everything and only on CleanUp i start to see whats necessary or not)
Unfortunately on this very script i couldnt and after rereading the documentation on the fine print(which wasnt so fine...) i read that SerializedObject can't be used in "production/runtime/build" version because it depended on UnityEditor which wouldnt exist on the finalized product.
Which really annoyed me because it was a very eloquent solution.
Suffix
The manual seems to suggest there is ways to go around using UnityEditor namespace. Unfortunately from their very vague tutorial i wasnt able to figure out how it works(im mentioning the tank example which only seems to use unityeditor because they wanted to be able to bind stuff on edit mode, while the binding itself seems to be done through uxml)
I've tried a few things but it all seemed out of context like having some serializedField would magically bind with uxml just because the binding path was the same as variable name
Then i thought well if unity doesnt want me to use editor stuff in runtime mode ill just force it so It shouldnt be that hard to just copy paste some of its class's and then somehow hack it. Unfortunetely Unity not only has a strict proprietary license that doesnt allow you to modify its software in anyway, but some of the annotations, functions, etc... were protected(especially the C stuff that they use)
Then i thought about doing it by hand and i arrived at two options:
Just put food.value = resources.food in some kind of update and hope it doesnt create any kind of issues when i migrate it to a multiplayer context
Or do something more complicated like some kind of delegate i would call that would update the UI, being more efficient in theory due to me only updating whats needed to.
Since im doing an RTS, i think the values will be changing constantly, so im very divided on both. Making me want to stick to the solution that was already done
This is all the more stressful when i hate how the documentation is structured, how difficult it is to go around the source code, and the worse how it feels like the documentation goes in length for behavior that is very similar to CSS
TL;DR:
Is there an alternative to BindProperty() in UI toolkit that doesn't rely on Unity Editor?
You could create a wrapper class to hold your values, which could invoke an event whenever the wrapped value changes.
public interface IProperty<T> : IProperty
{
new event Action<T> ValueChanged;
new T Value { get; }
}
public interface IProperty
{
event Action<object> ValueChanged;
object Value { get; }
}
[Serializable]
public class Property<T> : IProperty<T>
{
public event Action<T> ValueChanged;
event Action<object> IProperty.ValueChanged
{
add => valueChanged += value;
remove => valueChanged -= value;
}
[SerializeField]
private T value;
public T Value
{
get => value;
set
{
if(EqualityComparer<T>.Default.Equals(this.value, value))
{
return;
}
this.value = value;
ValueChanged?.Invoke(value);
valueChanged?.Invoke(value);
}
}
object IProperty.Value => value;
private Action<object> valueChanged;
public Property(T value) => this.value = value;
public static explicit operator Property<T>(T value) => new Property<T>(value);
public static implicit operator T(Property<T> binding) => binding.value;
}
After this you could create custom extension method similar to Unity's own BindProperty which works with this wrapper instead of a SerializedProperty.
public static class RuntimeBindingExtensions
{
private static readonly Dictionary<VisualElement, List<(IProperty property, Action<object> binding)>> propertyBindings = new Dictionary<VisualElement, List<(IProperty property, Action<object> binding)>>();
public static void BindProperty(this TextElement element, IProperty property)
{
if(!propertyBindings.TryGetValue(element, out var bindingsList))
{
bindingsList = new List<(IProperty, Action<object>)>();
propertyBindings.Add(element, bindingsList);
}
Action<object> onPropertyValueChanged = OnPropertyValueChanged;
bindingsList.Add((property, onPropertyValueChanged));
property.ValueChanged += onPropertyValueChanged;
OnPropertyValueChanged(property.Value);
void OnPropertyValueChanged(object newValue)
{
element.text = newValue?.ToString() ?? "";
}
}
public static void UnbindProperty(this TextElement element, IProperty property)
{
if(!propertyBindings.TryGetValue(element, out var bindingsList))
{
return;
}
for(int i = bindingsList.Count - 1; i >= 0; i--)
{
var propertyBinding = bindingsList[i];
if(propertyBinding.property == property)
{
propertyBinding.property.ValueChanged -= propertyBinding.binding;
bindingsList.RemoveAt(i);
}
}
}
public static void UnbindAllProperties(this TextElement element)
{
if(!propertyBindings.TryGetValue(element, out var bindingsList))
{
return;
}
foreach(var propertyBinding in bindingsList)
{
propertyBinding.property.ValueChanged -= propertyBinding.binding;
}
bindingsList.Clear();
}
}
Usage:
public class PlayerResources
{
public Property<int> food;
}
if(playerResources != null)
{
foodUI.BindProperty(playerResources.food);
}
UPDATE: Added also extension methods for unbinding properties and made BindProperty immediately update the text on the element.
So, i'm not sure what i'm doing wrong here but for some reason the callback function in TypeScript that i have doesn't have anything but _proto in the response's .data property whenever i set private properties in C# and new up an object that is filled with constructed properties. However, if the properties are public and i don't use a constructor then i can see the response's .data property is filled like i would expect it to be. Here is an example of what works:
public class ThisWorks{
public string MyProperty{get;set;}
}
Inside application layer:
ThisWorks example = new ThisWorks();
example.MyProperty = myReflectedProperty;
return example;
However, this does not work:
public class ThisDoesNotWork{
private string MyPrivateProperty {get;set;}
public ThisDoesNotWork(string myPrivateProperty){
MyPrivateProperty = myPrivateProperty;
}
}
What's causing this to happen? My TypeScript service has not changed but for some reason the data isn't coming across from the service call...Any help would be greatly appreciated! Also, Serialization is NOT constructive for this thread.
If you want to control access to a property, you can normally do this very will without a method in C#...
It appears that you want the ability to set the property, but not read it. The following is an example of a public write-only property, which you can read and write in private context.
This appears as a property to callers, rather than a method.
public class Example
{
private string _exampleProperty;
public string ExampleProperty
{
set { _exampleProperty = value; }
}
}
Please feel free to modify the title, I couldn't come up with any better one =\
Given the following example class
public class Person
{
public string Name;
public int ID;
public string City;
}
I need to create another mirror class, where every field is actually a wrapper of the original class:
public class PersonMirror
{
public FieldWrapper<string> Name;
public FieldWrapper<int> ID;
public FieldWrapper<string> City;
}
public class FieldWrapper<T>
{
public T Value;
public bool someBool;
public int someCounter;
// ..whatever
}
The thing is, I have many classes to mirror, and some of them have many fields! Moreover, the original class may be changed from time to time (add / remove / rename field), and every change must be applied to the mirrored class - not a good practice for maintainability.
My question is - is there a type safe way automate the decleration (rather then creation, such as generated code) of such mirrored classes?
EDIT:
Let's start from the beginning. In our SOA system, there is a resource access service (serviceX) responsible for updating items in the DB. Other services send it the modifications they would like to perform - in json that would be something like: {ID: 123, name : "myNewName"}. serviceX would then build an update query to send to the DB. However, there is a requirement that serviceX will expose a POCO interface, so that the interface will be language independent, so expressions such as (p=> p.name, "MyNewName") are not allowed. Another requirement is type safety, so json is not allowed either. Currently, the above solution is the best one we came up to answer all the requirements. Any better solutions are more then welcome!
IMO, there's no way to do what you want, except code generation.
Approaches for code generation could differ (this maybe source code generation + compilation, emitting IL code, either your own or existing one), but this is the only way.
use T4 to autogenerate your "WrapperClass".
Below, a proposition of how you could implement your FieldWrapper.
public class FieldWrapper<T, O>
{
private T _item;
private O _owner;
private PropertyInfo _setter;
public T Value
{
get { return _item; }
set {
if (!EqualityComparer<T>.Default.Equal(_item, value))
{
_item = value;
// do some personal check
_setter.SetValue(_owner, value);
}
}
}
public bool someBool;
public int someCounter;
// ..whatever
// CTOR
public FieldWrapper(O owner, Expression<Func<T, O>> propertyExpressionInTheOwner)
{
_owner = owner;
propertyName = (propertyExpressionInTheOwner.body as MemberExpression).Member.Name;
// get PropertyInfo using the owner and propertyName
}
}
Using the expression behavior permits you to create your fieldWrapper this way.
var p = new Person();
new FieldWrapper(p, (pers) => pers.Name);
The good point with this technique it is that if you person class change you will directly receive a compilation error.
With T4 the must is to load the assembly where all you class are, tag you class model with a specific attribute. Look into the assembly to found every class that have this attribute and generate the wrapper class associate.
You would have to run it after every code change, but you could create a code parsing application.
List desired keywords to substitute, such as " string ", " int ". Read the file, line by line. Find definition of classes (line contains "class"), then replace every instance of any given keyword in it with:
"FieldWrapper<" + keyword + ">"
You might want to drop keyword substitution inside methods (and perhaps in the method signatures / return types themselves) of by checking for "(" and ")", and the opening curly brace. Resume operation when you reach the closing curly brace. You can achieve that by storing the nesting level in an integer, incrementing it when hitting '{' and decrementing it when reaching '}'.
I'm currently working on some existing C# code and i simply want to set a property to null when a given code doesn't exist in the system.
The code i currently have looks like this:
if (!CodeExists(SomeClass.Code))
{
SomeClass.Code = null;
}
So assume that SomeClass.Code starts with a value of 100. It then checks if that Code exists with the method CodeExists(). If it can't find the code it should set SomeClass.Code = null.
But when i step through this piece of code with the debugger then i see that SomeClass.Code doesn't change at all, eventhough the debugger comes inside the if statement.
When i look at the property Code i see that it is declared as virtual:
public virtual CodeNumber Code { get; set; }
Does that mean i cannot simply change the value when it is declared as virtual? Is there anything i can do to change that value of Code?
Seems some other part of the code is the problem:
public SomeClassProjection SomeClass
{
get
{
// some stuff is done here
SomeClassState.Value = queryProcessor
.Execute(new ExistingProductsQuery { OrderNumber = SelectedOrderNumber });
return SomeClassState.Value;
}
}
So SomeClassState is returned. And that is defined like this:
public ViewValue<SomeClassProjection> SomeClassState;
So it does use another class like some of you suggested. And ViewValue clearly tells it is readyonly. That means i have to take another approach, but at least i now know what actually is prevents me from editting that property and that virtual has nothing to do with it.
About this topic:
So how do i accept an answer now that i found the solution? Or do i need to close this topic?
Seems some other part of the code is the problem:
public SomeClassProjection SomeClass
{
get
{
// some stuff is done here
SomeClassState.Value = queryProcessor
.Execute(new ExistingProductsQuery { OrderNumber = SelectedOrderNumber });
return SomeClassState.Value;
}
}
So SomeClassState is returned. And that is defined like this:
public ViewValue<SomeClassProjection> SomeClassState;
So it does use another class like some of you suggested. And ViewValue clearly tells it is readyonly. That means i have to take another approach, but at least i now know what actually is prevents me from editting that property and that virtual has nothing to do with it.
I swear I have seen an example of this but have been googling for a bit and can not find it.
I have a class that has a reference to an object and need to have a GET; method for it. My problem is that I do not want anyone to be able to fiddle with it, i.e. I want them to get a read only version of it, (note I need to be able to alter it from within my class).
Thanks
No, there's no way of doing this. For instance, if you return a List<string> (and it's not immutable) then callers will be able to add entries.
The normal way round this is to return an immutable wrapper, e.g. ReadOnlyCollection<T>.
For other mutable types, you may need to clone the value before returning it.
Note that just returning an immutable interface view (e.g. returning IEnumerable<T> instead of List<T>) won't stop a caller from casting back to the mutable type and mutating.
EDIT: Note that apart from anything else, this kind of concern is one of the reasons why immutable types make it easier to reason about code :)
Return a reference to a stripped-down interface:
interface IFoo
string Bar { get; }
class ClassWithGet
public IFoo GetFoo(...);
If the object isn't too complicated/extensive then write an wrapper around it.
for example:
class A {
public string strField = 'string';
public int intField = 10;
}
class AWrapper {
private A _aObj;
public AWrapper(A aobj) {
_aObj = A;
}
public string strField {
get {
return _aObj.strField;
}
}
public int intField {
get {
return _aObj.intField;
}
}
}
So now all you do is give your client code an instance of the AWrapper class so that they may only use what you allow them to see.
this may get a bit complicated and may not scale well if your base class is not set in stone, but for most simple situation it may just do the trick. I think this is called a facade pattern(but don't quote me on that =) )
This isn't possible. Get and set accessors to reference types get and set the reference to the object. You can prevent changes to the reference by using a private (or internal) setter, but you cannot prevent changes to the object itself if it's exposed by a getter.
Your question reads like you're looking for:
public PropertyName { get; private set; }
But then, given the answers so far I'm not sure I'm interpreting your question correctly. Besides, who am I to question Jon Skeet? :)
i agree with ReadOnlyCollection
See my simple code:
private List<Device> _devices;
public readonly System.Collections.ObjectModel.ReadOnlyCollection<Device> Devices
{
get
{
return (_devices.AsReadOnly());
}
}
ReadOnlyCollection dosen't has Add method so user cant add properties to it.BUT ther is no warranty that if user can modify objects by calling their methods....
I have faced this problem in a certain way.
I have a CategoryViewModel class, which have a property Category that I want private read-only :
public CategoryViewModel
{
private Category { get; }
}
In fact, I want it to be exported as read-only to other class. However I can't do such thing.
In my case (maybe it will help some other guys), I want to add it to a repository. The only way that I've found is to have a function with the repository as param 1, and an Action as param 2 :
public void ApplyAction(ICategoryRepository repo, Action<ICategoryRepository, Category> action)
{
action(repo, Category);
}
Like that, from elsewhere, I can do such thing :
categoryViewModel.ApplyAction(_repository, (r, c) => r.MarkForInsertOrUpdate(c));
This can help other to expose there property only for certains cases and can manage them.