PreviousPageModel is null - c#

I have a Xamarin.Forms application with freshmvvm framework. According to the documentation, I can use PreviousPageModel property of FreshBasePageModel base class to access data of the PageModel I navigated from. I navigate like this:
public FirstPageModel()
{
_validator = new CalculatorValidator();
CalculateCommand = new Command(execute: () =>
{
ValidationResult = _validator.Validate(this);
RaisePropertyChanged(nameof(ValidationResult));
if (ValidationResult.IsValid)
{
CoreMethods.PushPageModel<SecondPageModel>();
}
});
}
The navigation happens, but in the SecondPageModel constructor the PreviousPageModel is null:
public SecondPageModel()
{
_previousModel = (FirstPageModel)PreviousPageModel;
}
What am I doing wrong?
Thank you.
EDIT:
I also tried:
public FirstPageModel()
{
_validator = new CalculatorValidator();
CalculateCommand = new Command(Calculate);
}
private void Calculate()
{
ValidationResult = _validator.Validate(this);
RaisePropertyChanged(nameof(ValidationResult));
if(ValidationResult.IsValid)
{
CoreMethods.PushPageModel<SecondPageModel>(this);
}
}

The issue is that until this point in time, In your PageModel lifecycle the PreviousPageModelproperty this not assigned a value.
Now I am sure that this property is available by the time the Init lifecycle method is called i.e.
public override void Init(object initData)
{
_previousModel = (FirstPageModel)PreviousPageModel;
}
But if you do not want to do this and want it to be strictly in your constructor what you can do is call the base implementation and hope that our friend micheal has assigned this property there:
Something like below:
public SecondPageModel() : base()
{
}

I got the answer here:
https://forums.xamarin.com/discussion/comment/365262/#Comment_365262
The PreviousPageModel is null because it hasn't been set in the constructor. Place your code in the ViewIsAppearing lifecycle event, then you will get the correct previous model:
protected override void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
_previousModel = (FirstPageModel)PreviousPageModel;
}

Related

Caliburn.Micro Navigation from Usercontrol over ShellViewModel

Description
I use the Caliburn.Micro in version 4 and try to navigatie from a Usercontrol to another page. The ShellView.xaml has a <ContentControl x:Name="ActiveItem" /> element and all navigationmethods like DashboardView(), GcsImportView()... work aslong I am inside the ShellView.xaml. But if I call from a Usercontrol (inside the ContentControl) nothing happens. I don´t even get a error. I can push the Button thousend times without any respond. I hope somebody can help me here.
Update
Even if I try the code from these site it doesn´t work. On debugging the ActiveItem value will be changed. That looks like a bug?
ShellViewModel.cs
namespace GCS.ViewModels
{
public class ShellViewModel : Conductor<object>//, IHandle<GcsImportProgressViewModel>
{
private string _version = "v. " + Assembly.GetExecutingAssembly().GetName().Version.ToString();
public string Version
{
get { return _version; }
}
public ShellViewModel(/*IEventAggregator eventAggregator*/)
{
DashboardView();
//eventAggregator.SubscribeOnUIThread(this);
}
public void DashboardView() => ActivateItemAsync(new DashboardViewModel());
public void GcsImportView() => ActivateItemAsync(IoC.Get<GcsImportViewModel>());
public void GcsExportView() => ActivateItemAsync(new GcsExportViewModel());
public void ChangeView<T>() => ActivateItemAsync(IoC.Get<T>());
//public Task HandleAsync(GcsImportProgressViewModel message, CancellationToken cancellationToken)
//{
// throw new NotImplementedException();
//}
}
}
UserControl Constructor
public GcsImportViewModel(ShellViewModel shell, IGcsRepository gcsRepository/)
{
filePath = "Bitte GCS Excel Datei auswählen";
databases = new ObservableCollection<GcsTable>(gcsRepository.GetAllTables());
selectedDatabase = databases.FirstOrDefault();
this.gcsRepository = gcsRepository;
}
Usercontrol Method to change the view
public void ClickImport()
{
shell.ChangeView<GcsImportProgressViewModel>();
}
It seems like you are calling ChangeView on a different instance of the ShellViewModel.
You should register the view model as a singleton in your bootstrapper:
container.Singleton<ShellViewModel, ShellViewModel>();

Passing data to fragment viewmodel mvvmcross

so I have an activity that has a tabbed page with 2 fragments.
public class RecipeDetailActivity : BaseFragmentActivity<RecipeDetailViewModel>
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.RecipeDetailView);
AttachActionBar();
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.Title = "Recipe details";
var viewPager = FindViewById<ViewPager>(Resource.Id.main_view_pager);
if (viewPager != null)
{
var fragments = new List<MvxViewPagerFragmentInfo>();
fragments.Add(
new MvxViewPagerFragmentInfo("Ingrediente", typeof(RecipeFlavoursFragment), typeof(RecipeFlavoursViewModel)));
fragments.Add(
new MvxViewPagerFragmentInfo("Flavours", typeof(RecipeIngridientsFragment), typeof(RecipeIngridientsViewModel)));
viewPager.Adapter = new MvxFragmentPagerAdapter(this, SupportFragmentManager, fragments);
viewPager.Adapter = new MvxFragmentPagerAdapter(this, SupportFragmentManager, fragments);
var tabLayout = FindViewById<TabLayout>(Resource.Id.main_tablayout);
tabLayout.SetupWithViewPager(viewPager);
}
}
}
I show this page using the following code.
private void SelectRecipe(RecipeModel recipe)
{
var recipeJson = JsonConvert.SerializeObject(recipe);
ShowViewModel<RecipeDetailViewModel>(new { recipe = recipeJson });
}
Now what I would like is to pass some data to child view models.
RecipeFlavoursViewModel
RecipeIngridientsViewModel
I've tried so far :
Using parameterValueObject
fragments.Add(
new MvxViewPagerFragmentInfo("Ingrediente", typeof(RecipeFlavoursFragment), typeof(RecipeFlavoursViewModel), new { recipe = ViewModel.Recipe }));
Using IMvxBundle
In RecipeDetailViewModel
protected override void SaveStateToBundle(IMvxBundle bundle)
{
bundle.Data["Recipe"] = JsonConvert.SerializeObject(Recipe);
base.SaveStateToBundle(bundle);
}
In RecipeIngridientsViewModel
protected override void InitFromBundle(IMvxBundle parameters)
{
base.InitFromBundle(parameters);
if (parameters.Data.Count != 0)
{
Recipe = JsonConvert.DeserializeObject<RecipeModel>(parameters.Data["recipe"]);
}
}
None of them have worked so far. Any ideas what am I doing wrong? Do I have to use the navigation service from MvvmCross 5 to be able to use InitFromBundle and SaveStateToBundle.
InitFromBundle it's called everytime my fragments is displayed, but SaveStateToBundle from RecipeDetailViewModel never gets called.
In order to do this, you could take advantage of the MvxViewPagerFragmentPresentationAttribute so that the Presenter takes the responsibility to show the fragments and you just show the ViewModels passing the Recipe parameter as any other but it has some minor bugs for the moment.
However one way to solve this is to have in your RecipeDetailViewModel properties with the fragments' ViewModels you want to have in your ViewPager and load them in the Initialize so then you can reference them from your RecipeDetailActivity:
Using Mvx 5 you can use new Navigation to show the ViewModels. If the details are openned from RecipeListViewModel then:
public class RecipeDetailViewModelArgs
{
public RecipeDetailViewModelArgs(RecipeModel recipe)
{
this.Recipe = recipe;
}
public RecipeModel Recipe { get; }
}
public class RecipeListViewModel : MvxViewModel
{
private readonly IMvxNavigationService navigationService;
public RecipeListViewModel(IMvxNavigationService navigationService)
{
this.navigationService = navigationService;
}
private async Task SelectRecipe(RecipeModel recipe)
{
await this.navigationService.Navigate<RecipeDetailViewModel, RecipeDetailViewModelArgs>(new RecipeDetailViewModelArgs(recipe));
}
}
Then in your details ViewModel you just cache the recipe, load the children ViewModels (ingredients and flavours) and set the recipe to them:
public class RecipeDetailViewModel : MvxViewModel<RecipeDetailViewModelArgs>
{
private readonly IMvxViewModelLoader mvxViewModelLoader;
private readonly IMvxJsonConverter jsonConverter;
private RecipeModel recipe;
public RecipeDetailViewModel(IMvxViewModelLoader mvxViewModelLoader, IMvxJsonConverter jsonConverter)
{
this.mvxViewModelLoader = mvxViewModelLoader;
this.jsonConverter = jsonConverter;
}
public override void Prepare(RecipeDetailViewModelArgs parameter)
{
this.recipe = parameter.Recipe;
}
protected override void SaveStateToBundle(IMvxBundle bundle)
{
base.SaveStateToBundle(bundle);
bundle.Data["RecipeKey"] = this.jsonConverter.SerializeObject(this.recipe);
}
protected override void ReloadFromBundle(IMvxBundle state)
{
base.ReloadFromBundle(state);
this.recipe = this.jsonConverter.DeserializeObject<RecipeModel>(state.Data["RecipeKey"]);
}
public override async Task Initialize()
{
await base.Initialize();
this.InitializeChildrenViewModels();
}
public RecipeFlavoursViewModel FlavoursViewModel { get; private set; }
public RecipeIngridientsViewModel IngredientsViewModel { get; private set; }
protected virtual void InitializeChildrenViewModels()
{
// Load each childre ViewModel and set the recipe
this.FlavoursViewModel = this.mvxViewModelLoader.LoadViewModel(new MvxViewModelRequest<RecipeFlavoursViewModel>(null, null), null);
this.FlavoursViewModel.Recipe = this.recipe;
this.IngredientsViewModel = this.mvxViewModelLoader.LoadViewModel(new MvxViewModelRequest<RecipeIngridientsViewModel>(null, null), null);
this.FlavoursViewModel.Recipe = this.recipe;
}
}
Then when you load the ViewPager you can take advantage from the other constructor of MvxViewPagerFragmentInfo => public MvxViewPagerFragmentInfo (string title, string tag, Type fragmentType, IMvxViewModel viewModel, object parameterValuesObject = null) so you can pass the ViewModels previously loaded:
this.viewPager = view.FindViewById<ViewPager>(Resource.Id.viewPagerDetails);
if (viewPager != null)
{
var fragments = new List<MvxViewPagerFragmentInfo>();
fragments.Add(new MvxViewPagerFragmentInfo("Ingredients", "RecipeIngridientsViewModelTag", typeof(RecipeIngridientsView), this.ViewModel.IngridientsViewModel));
fragments.Add(new MvxViewPagerFragmentInfo("Flavours", "RecipeFlavoursViewModelTag", typeof(RecipeFlavoursView), this.ViewModel.FlavoursViewModel));
this.viewPager.Adapter = new MvxFragmentPagerAdapter(this.Activity, this.ChildFragmentManager, fragments);
}
That's it.
BTW if you don't want to use Navigation or you are not using Mvx 5.x then you just Initialize the children ViewModels in the void Start() method.
And to conclude if you want to change values of your Recipe from the children one simple way is to have a Singleton initialized with your Recipe and then you just Inject the singleton in the constructors so you always have the reference to the same Recipe and you don't have to pass the Recipe object forth & back to those ViewModels and merge the changes made from each of them. More info in MvvmCross: Accessing models by reference from everywhere
HIH

In MVVMCross, is it possible to close a viewmodel and pass values back to the previous viewmodel in the navigation stack?

Consider the following example. I have three view models, ViewModel_A, ViewModel_B, and ViewModel_Values.
I want to be able to navigate to ViewModel_Values from either ViewModel_A or ViewModel_B, select a value from ViewModel_Values, then return that value to the calling view model.
Is there a way of passing arguments to previous view models in the navigation stack so that I can simply call ViewModel_Values.Close(this), thereby ensuring that the ViewModels_Values is decoupled from any other view models and can be used with arbitrary "parent" view models?
MvvmCross 5 onwards
From MvvmCross 5 you can use the new IMvxNavigationService that allows you to have a much richer navigation. One of the new features is the possibility to await a value from another ViewModel after navigating to it and should be the approach to take after MvvmCross 5 instead of Messenger, e.g.:
public class ViewModel_A : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public ViewModel_A(IMvxNavigationService navigation)
{
_navigationService = navigationService;
}
public override async Task Initialize()
{
//Do heavy work and data loading here
}
public async Task SomeMethod()
{
var result = await _navigationService.Navigate<ViewModel_Values, MyObject, MyReturnObject>(new MyObject());
//Do something with the result MyReturnObject that you get back
}
}
public class ViewModel_Values : MvxViewModel<MyObject, MyReturnObject>
{
private readonly IMvxNavigationService _navigationService;
public ViewModel_Values(IMvxNavigationService navigation)
{
_navigationService = navigationService;
}
public override void Prepare(MyObject parameter)
{
//Do anything before navigating to the view
//Save the parameter to a property if you want to use it later
}
public override async Task Initialize()
{
//Do heavy work and data loading here
}
public async Task SomeMethodToClose()
{
// here you returned the value
await _navigationService.Close(this, new MyReturnObject());
}
}
More info here
HIH
Use messaging center. Here is the sample code.
//for trigger
MessagingCenter.Send<object> (this, "Hi");
//put this where you want to receive your data
MessagingCenter.Subscribe<object> (this, "Hi", (sender) => {
// do something whenever the "Hi" message is sent
});
Installing & using the MvxMessenger plugin is a great way to decouple view model communication in MvvmCross -
In your case, you could set up a new message -
public class ValuesChangedMessage : MvxMessage
{
public ValuesChangedMessage(object sender, int valuea, string valueb)
: base(sender)
{
Valuea = valuea;
Valueb = valueb;
}
public int Valuea { get; private set; }
public string Valueb { get; private set; }
}
In ViewModel_Values, you would act on / publish your UX changes with -
_mvxMessenger.Publish<ValuesChangedMessage>(new ValuesChangedMessage(this, 1, "boo!"));
And in ViewModel_A, ViewModel_B you would subscribe and act on them (as your ViewModel A / B would be still in the navigation stack when you pushed ViewModel_Values from them, so they could receive the message) -
private MvxSubscriptionToken _messageToken;
_messageToken = _mvxMessenger.Subscribe<ValuesChangedMessage>(async message =>
{
// use message.Valuea etc ..
});
More infos here -
https://www.mvvmcross.com/documentation/plugins/messenger?scroll=644
https://www.youtube.com/watch?feature=player_embedded&v=HQdvrWWzkIk
In my case of trying to navigate in this pattern:
//pseudo code
"ModelA" => "ModelB<List<MyObject>>" => "ModelC<MyObject>"
OR
//pseudo code
"ModelA" => "ModelC<MyObject>"
I used the following work around in my ViewDestroy() override of ModelB<List>:
private bool destroyView = true;
public bool DestroyView
{
get => destroyView;
set
{
destroyView = value;
RaisePropertyChanged(() => DestroyView);
}
}
public override void ViewDestroy(bool viewFinishing)
{
viewFinishing = DestroyView;
base.ViewDestroy(viewFinishing);
}
private async Task ModifySelectedObject()
{
DestroyView = false;
MyObject obj = SelectedObject;
MyObject modifiedObj = await _navigationService.Navigate<ModifySingleViewModel, MyObject, MyObject>(new MyObject());
if (modifiedObj != null)
{
obj = modifiedObj;
}
else
{
await Application.Current.MainPage.DisplayAlert("", "No changes made.", "OK");
}
DestroyView = true;
}
This keeps the original
"await _navigationService.Navigate<ModifyMultipleViewModel,
List, List>(new MyObject);"
from ModelA open when navigating to ModelC from ModelB, but still allows the ViewDestroy Method to close otherwise.

Weak References stays alive

I have an interface: IRemoteDataChangedListener
public interface IRemoteDataChangedListener<TData>
{
void DataReceived(TData newData);
}
And a class, RealtimeEventService
public class RealtimeEventService : IRealtimeEventService
{
private readonly IEventListener listener;
private readonly List<Tuple<Type, WeakReference>> dataCreated;
public RealtimeEventService(IEventListener eventListener)
{
this.dataCreated = new List<Tuple<Type, WeakReference>>();
this.listener = eventListener;
this.listener.EventReceived += this.ListenerOnEventReceived;
}
private void ListenerOnEventReceived(EventMessage message)
{
Type type = message.GetType();
if (type == typeof(NotificationReadEventMessage))
{
this.DataChanged((NotificationReadEventMessage)message);
}
}
public void SubscribeDataChanged<TEventMessage>(IRemoteDataChangedListener<TEventMessage> dataChangedListener) where TEventMessage : EventMessage, new()
{
this.dataCreated.Add(Tuple.Create(typeof(TEventMessage), new WeakReference(dataChangedListener)));
}
internal void DataChanged<TKey>(TKey newData)
where TKey : class, new()
{
LoopAndFilter<TKey>(this.dataCreated, listener => listener.DataReceived(newData));
}
private static void LoopAndFilter<TKey>(ICollection<Tuple<Type, WeakReference>> collection,
Action<IRemoteDataChangedListener<TKey>> success) where TKey : class
{
foreach (var reference in collection.ToArray())
{
if (!reference.Item2.IsAlive)
{
collection.Remove(reference);
continue;
}
if (reference.Item1 != typeof(TKey))
continue;
success((IRemoteDataChangedListener<TKey>)reference.Item2.Target);
}
}
#endregion
}
Whenever I create a test class that inherits IRemoteDataChangedListener with NotificationReadEventMessage as generic argument, and use an instance of this class with SubscribeDataChanged(), it gets hooked up just fine, and the method gets called.
Problem is, when I set the instance reference to null and run GC.Collect(), it should then be null, and the next time RealtimeEventService's LoopAndFilter method runs, it should detect that it is no longer alive, and remove the Weakreference from the list.
However it does not. When I inspect the value (In LoopAndFilter), after setting the instance reference to null in the test, the value still shows up as Alive being true.
And now I've been staring at this code for hours, and I simply cannot find anywhere I'd have a strong reference to the class...
Any help?
#Edit: Unit test (Using the Moq and Should libraries):
public class RealtimeEventServiceTests
{
[Fact]
public void VerifyWeakReferencesWorksAsIntended()
{
var eventListenerMock = new Mock<IEventListener>();
IRealtimeEventService service = new RealtimeEventService(eventListenerMock.Object);
bool called = false;
RemoteDataTest dataChangedListener = new RemoteDataTest();
dataChangedListener.Called += (sender, args) => called = true;
service.SubscribeDataChanged(dataChangedListener);
called.ShouldBeFalse();
((RealtimeEventService)service).DataChanged(new NotificationReadEventMessage());
called.ShouldBeTrue();
called = false;
dataChangedListener = null;
GC.Collect();
called.ShouldBeFalse();
((RealtimeEventService)service).DataChanged(new NotificationReadEventMessage());
called.ShouldBeFalse();
}
}
public class RemoteDataTest : IRemoteDataChangedListener<NotificationReadEventMessage>
{
public event EventHandler Called;
public void DataReceived(NotificationReadEventMessage newData)
{
if (Called != null) Called(this, null);
}
}
As it turns out, when I got home and compiled it at home, it ran just fine. And when I got back to work, it worked fine there as well.
Guess it's just one of those spooky bugs that magically vanish at inexplicable times. I'm just glad to be rid of it.
I did take the advice of Ewan & Scott Chamberlain, so thanks for that!

How to access private members of superclass in override?

I want to inherit from NHibernate's SqlClientBatchingBatcher class exactly like this (code taken from TooManyRowsAffectedException with encrypted triggers):
public class NonBatchingBatcherWithoutVerification : SqlClientBatchingBatcher
{
public NonBatchingBatcherWithoutVerification(ConnectionManager connectionManager, IInterceptor interceptor) : base(connectionManager, interceptor)
{}
protected override void DoExecuteBatch(IDbCommand ps)
{
log.DebugFormat("Executing batch");
CheckReaders();
Prepare(currentBatch.BatchCommand);
if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
{
Factory.Settings.SqlStatementLogger.LogBatchCommand(currentBatchCommandsLog.ToString());
currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
}
int rowsAffected = currentBatch.ExecuteNonQuery();
// Removed the following line
//Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected);
currentBatch.Dispose();
totalExpectedRowsAffected = 0;
currentBatch = new SqlClientSqlCommandSet();
}
}
Just notice some of the members accessed in the method here (like currentBatch or totalExpectedRowsAffected).
Well, it turns out these members are actually private in the superclass of the current NHibernate 3.3 source. So how do I effectively inherit the class without copying the whole thing? This is the unmodified NHibernate code of the class by the way:
public class SqlClientBatchingBatcher : AbstractBatcher
{
private int _batchSize;
private int _totalExpectedRowsAffected;
private SqlClientSqlCommandSet _currentBatch;
private StringBuilder _currentBatchCommandsLog;
private readonly int _defaultTimeout;
public SqlClientBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
: base(connectionManager, interceptor)
{
_batchSize = Factory.Settings.AdoBatchSize;
_defaultTimeout = PropertiesHelper.GetInt32(Cfg.Environment.CommandTimeout, Cfg.Environment.Properties, -1);
_currentBatch = CreateConfiguredBatch();
//we always create this, because we need to deal with a scenario in which
//the user change the logging configuration at runtime. Trying to put this
//behind an if(log.IsDebugEnabled) will cause a null reference exception
//at that point.
_currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
}
public override int BatchSize
{
get { return _batchSize; }
set { _batchSize = value; }
}
protected override int CountOfStatementsInCurrentBatch
{
get { return _currentBatch.CountOfCommands; }
}
public override void AddToBatch(IExpectation expectation)
{
_totalExpectedRowsAffected += expectation.ExpectedRowCount;
IDbCommand batchUpdate = CurrentCommand;
Driver.AdjustCommand(batchUpdate);
string lineWithParameters = null;
var sqlStatementLogger = Factory.Settings.SqlStatementLogger;
if (sqlStatementLogger.IsDebugEnabled || Log.IsDebugEnabled)
{
lineWithParameters = sqlStatementLogger.GetCommandLineWithParameters(batchUpdate);
var formatStyle = sqlStatementLogger.DetermineActualStyle(FormatStyle.Basic);
lineWithParameters = formatStyle.Formatter.Format(lineWithParameters);
_currentBatchCommandsLog.Append("command ")
.Append(_currentBatch.CountOfCommands)
.Append(":")
.AppendLine(lineWithParameters);
}
if (Log.IsDebugEnabled)
{
Log.Debug("Adding to batch:" + lineWithParameters);
}
_currentBatch.Append((System.Data.SqlClient.SqlCommand) batchUpdate);
if (_currentBatch.CountOfCommands >= _batchSize)
{
ExecuteBatchWithTiming(batchUpdate);
}
}
protected override void DoExecuteBatch(IDbCommand ps)
{
Log.DebugFormat("Executing batch");
CheckReaders();
Prepare(_currentBatch.BatchCommand);
if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
{
Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString());
_currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
}
int rowsAffected;
try
{
rowsAffected = _currentBatch.ExecuteNonQuery();
}
catch (DbException e)
{
throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command.");
}
Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected);
_currentBatch.Dispose();
_totalExpectedRowsAffected = 0;
_currentBatch = CreateConfiguredBatch();
}
private SqlClientSqlCommandSet CreateConfiguredBatch()
{
var result = new SqlClientSqlCommandSet();
if (_defaultTimeout > 0)
{
try
{
result.CommandTimeout = _defaultTimeout;
}
catch (Exception e)
{
if (Log.IsWarnEnabled)
{
Log.Warn(e.ToString());
}
}
}
return result;
}
}
Did I overlook something? Seems to a rather bad approach to copy the whole thing just to override all access to any private members. I just want to override one method!
There is only one way to legally access private members of your base class: put the derived class inside the base class:
class Base
{
private int x;
private class Derived : Base
{
private void M()
{
Console.WriteLine(this.x); // legal!
}
}
}
Of course, if you could put the class inside the base class then you could also rewrite the base class so that the members were protected.
That the original author made the members private is a hint to you that the class was not designed for you to muck around with that data.
If they're set as private, there's really nothing (short of using Reflection, which is ugly and certainly not always safe) that you can do.
Private members of a superclass cannot be accessed, bcause they are private. Encapsulation in OOP is there to prohibit this direct access and so ensure that objects function properly.
There might be properties to access the private members, These are the ones you can use to read from/write to private members. The properties will ensure that no harm to the object will be done.
You can access private fields, properties and methods of a parent class using reflection (for example, accessing a field as described here: Reflecting a private field from a base class)
This is not safe, however as the idea of private is that the library implementation could change and those private methods, fields and properties could change or disappear. If they change the implementation, an update could break your code.
That said, I've done it a few times myself. You just need to weigh the risk.

Categories