I want to check whether a particular runtime type contains a property with a certain attribute like this:
public void Audit(MongoStorableObject oldVersion, MongoStorableObject newVersion)
{
if(oldVersion.GetType() != newVersion.GetType())
{
throw new ArgumentException("Can't Audit versions of different Types");
}
foreach(var i in oldVersion.GetType().GetProperties())
{
//The statement in here is not valid, how can I achieve look up of a particular attribute
if (i.GetCustomAttributes().Contains<Attribute>(MongoDB.Bson.Serialization.Attributes.BsonIgnoreAttribute)) continue;
//else do some actual auditing work
}
}
But the statement is not valid, Can you tell me how to achieve lookup of a particular attribute on a property like this? Thanks,
Update:
I've found this which doesn't make intellisense complain:
if (i.GetCustomAttributes((new MongoDB.Bson.Serialization.Attributes.BsonIgnoreAttribute()).GetType(),false).Length > 0) continue;
But I'm still not certain this will do what I want it too.
Change:
if (i.GetCustomAttributes().Contains<Attribute>(MongoDB.Bson.Serialization.Attributes.BsonIgnoreAttribute)) continue;
to
if (i.GetCustomAttributes().Any(x=> x is MongoDB.Bson.Serialization.Attributes.BsonIgnoreAttribute)) continue;
Revised:
public void Audit(MongoStorableObject oldVersion, MongoStorableObject newVersion)
{
if(oldVersion.GetType() != newVersion.GetType())
{
throw new ArgumentException("Can't Audit versions of different Types");
}
foreach(var i in oldVersion.GetType().GetProperties())
{
//The statement in here is not valid, how can I achieve look up of a particular attribute
if (i.GetCustomAttributes().Any(x=> x is MongoDB.Bson.Serialization.Attributes.BsonIgnoreAttribute)) continue;
//else do some actual auditing work
}
}
To clarify:
GetCustomAttributes() returns a list of attribute objects on the property. You need to iterate through them and check whether any of their TYPES are of BsonIgnoreAttribute.
private static void PrintAuthorInfo(System.Type t)
{
System.Console.WriteLine("Author information for {0}", t);
// Using reflection.
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t); // Reflection.
// Displaying output.
foreach (System.Attribute attr in attrs)
{
if (attr is Author)
{
Author a = (Author)attr;
System.Console.WriteLine(" {0}, version {1:f}", a.GetName(), a.version);
}
}
}
http://msdn.microsoft.com/en-us/library/z919e8tw.aspx
Related
I have these lines of code in my .NET 4.7.2 server application:
object saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly); // this works
((AxdSalesOrder)saveObject).SalesTable[0].TableDlvAddr = null; // this throws error
I can execute the null-set (2nd line above) in the VS2019 Watch Window and it works perfectly and achieves the desired effect. But when I allow it to run in normal execution (whether in debug mode or not), I get an unhandled exception on that 2nd line:
Unable to cast object of type 'AxdSalesOrder' to type
'Elogix.MSAx.Core.Ax2012ElogixServices.AxdSalesOrder'
There is dynamic stuff going in in relation to that type:
public override object GetEntityAsNativeObject(MSAxEntity entity, Assembly dynamicProxyAssembly) {
var salesOrderObject = Activator.CreateInstance(dynamicProxyAssembly.GetType("AxdSalesOrder"));
var salesOrderTable = DynamicEntityUtil.CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly, "AxdEntity_SalesTable");
Array tableLines =
Array.CreateInstance(
salesOrderObject.GetType().GetProperty("SalesTable").PropertyType.GetElementType(), 1);
tableLines.SetValue(salesOrderTable, 0);
salesOrderObject.SetPropertyValue("SalesTable", tableLines);
return salesOrderObject;
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Assembly dynamicProxyAssembly, string objectTypeName) {
return CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly.GetType(objectTypeName));
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Type type) {
if (type == null) {
throw new ArgumentException("Cannot create object from dynamic entity because \"Type\" is null.");
}
if (type.IsArray) {
return CreateArrayFromDynamicEntity(entity, type);
}
return CreateClassFromDynamicEntity(entity, type);
}
private static object CreateClassFromDynamicEntity(DynamicEntity entity, Type type) {
var nativeObject = Activator.CreateInstance(type);
// this will recursively convert the dynamic values to the native type values on the object.
updateValuesFromDynamicValues(entity);
var modifiedProperties = from property in entity.Properties
//where property.State != DynamicPropertyState.Unchanged
select property;
foreach(var property in modifiedProperties) {
Type valueUnderlyingType = Nullable.GetUnderlyingType(property.Type);
if (valueUnderlyingType != null && valueUnderlyingType.IsEnum) {
PropertyInfo info = nativeObject.GetType().GetProperty(property.Name);
Type targetUnderlyingType = Nullable.GetUnderlyingType(info.PropertyType);
if (property.Value == null) {
info.SetValue(nativeObject, null, null);
} else {
object correctedValue = property.Value.CorrectedEnumValue(targetUnderlyingType);
info.SetValue(nativeObject, correctedValue, null);
}
} else if (property.Type.IsEnum) {
if (property.Value == null) {
continue;
}
object correctedValue = property.Value.CorrectedEnumValue(property.Type);
nativeObject.SetPropertyValue(property.Name, correctedValue);
} else {
try {
nativeObject.SetPropertyValue(property.Name, property.Value);
} catch (Exception ex) {
Log.Write(ex.Message);
}
}
}
return nativeObject;
}
Here is how it looks in the VS2019 Watch Window:
Did this in Immediate Window:
var t = saveObject.GetType();
t.FullName
"AxdSalesOrder"
As you can see, the type's FullName is not very full, not qualified by anything, due to the dynamic nature of the type.
I can try it this way:
(saveObject as AxdSalesOrder).SalesTable[0].TableDlvAddr = null;
Again, that works in Watch, but throws this exception when run in normal execution:
Object reference not set to an instance of an object.
Clearly, VS/Watch knows the type, which is why it can execute without errors inside Watch. But the .net runtime apparently doesn't know the type at run time. How can I get this object cast to work in normal code execution like it does when run in Watch?
The answer here is to take advantage of dynamic typing since the static type is not available here:
dynamic saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly);
saveObject.SalesTable[0].TableDlvAddr = null;
Another possible approach would be to use reflection, however since the expression applied to the object involves two properties and an index lookup (.SalesTable[0].TableDlvAddr) this would look much less readable.
The GetEntityAsNativeObject could also benefit from this style, you could consider rewriting it to using dynamic binding rather than reflection.
Ive created a tool for my team which allows them to view an object visually by using the Dump() method of LinqPad. This is purely for use by the development teams to make things easier rather than having to attach a debugger which isnt always possible.
They enter the name of the object they want to Dump and the program does it in its current context.
eg. User enters Sales.CurrentUser and the application visualises the CurrentUser object using LinqPad's Dump() method.
public void VisualiseObject()
{
object CurrentObject = this;
string Result = GetObjectFromUser("Enter Propery or Field path eg. Sales.CurrentUser", "");
if (Result == null)
return;
else if (Result != string.Empty)
{
string[] Objects = Result.Split(".".ToCharArray());
try
{
foreach (string objName in Objects)
{
CurrentObject = GetPropValue(CurrentObject, objName);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
if (CurrentObject == null)
{
MessageBox.Show("Object is null");
return;
}
}
CurrentObject.Dump(Result);
}
public static object GetPropValue(object src, string propName)
{
object result = null;
try
{
result = src.GetType().GetProperty(propName).GetValue(src, null);
}
catch
{
try
{
result = src.GetType().GetField(propName).GetValue(src);
}
catch (Exception)
{
result = null;
}
}
return result;
}
Anyway im trying to think of a way to support the user supplying an index or key to a array, list or dictionary object and handle this in reflection.
The user could supply for example. An array or list Sales.AllUsers[0] or for dictionary Sales.Items["Water"]
Any ideas?
You can try the Dynamic Linq Expressions, it allows you to do parse expressions (just be carefull with code injection).
Or you can try accessing the Get method of the array:
(given that prop is a variable of type int[])
var getter = array.GetType().GetMethod("Get");
var value = getter.Invoke(array, new object[] { index });
Haven't tested with Dictionary or Collection but should also work in the same way.
I'm new to c# and working on my first project - a console app. I am having some trouble understanding why my code will not return false when an entry in the address book already exists. The following methods are all part of the AddressBook class CheckEntry(), AddEntry(), RemoveEntry().
Ok, so the boolean method CheckEntry() is used by two other methods - AddEntry() and RemoveEntry() - both are looking to verify if the user entry exists before performing their respective duties. In AddEntry() it is supposed to see if the contact already exists before adding another contact and shouldn't create the contact if it does (but it's adding duplicates). RemoveEntry() is supposed to check if it exists and uses the updated value of the stored variable in CheckEntry() to remove the current contact (but does nothing).
I know I'm probably either missing something simple or have way over thought the whole process. My assumption is that checkEntry() is not working correctly since both of the functions tied to it are faulting. Anyone have any ideas?? Let me know if I need to explain anything further.
NOTE: I know that using a list would be more efficient/easier to work with. It was my goal to use an array for learning purposes. Switching from learning Javascript to C# has been a bit of a challenge and I want to make sure I'm learning each thing before moving onto the next.
Here is all of my code. Each class is separated by "//--------" thank you in advance for your help.
namespace AddressBook {
class Contact {
public string Name;
public string Address;
public Contact(string name, string address) {
Name = name;
Address = address;
}
}
}
//------------------------------------------------------------------------
using System;
namespace AddressBook {
class AddressBook {
public readonly Contact[] contacts;
public AddressBook() {
contacts = new Contact[2]; ;
}
public void AddEntry(string name, string address) {
Contact AddContact = new Contact(name, address);
if (CheckEntry(name)) {
for (int i = 0; i < contacts.Length; i++) {
if (contacts[i] == null) {
contacts[i] = AddContact;
Console.WriteLine("Address Book updated. {0} has been added!", name);
break;
}
}
}
}
private string existingContact = "";
private bool CheckEntry(string name) {
foreach(Contact contact in contacts) {
if (contact == null) {
break;
}
else if (contact != null && contact.ToString() != name) {
continue;
}
else if (contact.ToString() == name) {
existingContact = contact.ToString();
return false;
}
}
return true;
}
public void RemoveEntry(string name) {
if( !(CheckEntry(name)) ) {
existingContact = null;
Console.WriteLine("{0} removed from contacts", name);
}
}
public string View() {
string contactList = "";
foreach(Contact contact in contacts) {
if(contact == null) {
break;
}
contactList += String.Format("Name: {0} -- Address: {1}" + Environment.NewLine, contact.Name, contact.Address);
}
return contactList;
}
}
}
//------------------------------------------------------------------------
using System;
namespace AddressBook {
class Program {
static void Main(string[] args) {
AddressBook addressBook = new AddressBook();
PromptUser();
void Menu() {
Console.WriteLine("TYPE:");
Console.WriteLine("'Add' to add a contact: ");
Console.WriteLine("'Remove' to select and remove a contact: ");
Console.WriteLine("'Quit' to exit: ");
}
void UpdateAddressBook(string userInput) {
string name = "";
string address = "";
switch ( userInput.ToLower() ) {
case "add":
Console.Write("Enter a name: ");
name = Console.ReadLine();
Console.Write("Enter an address: ");
address = Console.ReadLine();
addressBook.AddEntry(name, address);
break;
case "remove":
Console.Write("Enter a name to remove: ");
name = Console.ReadLine();
addressBook.RemoveEntry(name);
break;
case "view":
Console.WriteLine(addressBook.View());
break;
}
}
void PromptUser() {
Menu();
string userInput = "";
while (userInput != "quit") {
Console.WriteLine("What would you like to do?");
userInput = Console.ReadLine();
UpdateAddressBook(userInput);
}
}
}
}
}
Here is what I came up with - tested changes
Now I can't add duplicate names and I can remove entries.
public void AddEntry(string name, string address) {
Contact AddContact = new Contact(); //changed
AddContact.Name = name; //changed
AddContact.Address = address; //changed
if (CheckEntry(name)) {
for(int i = 0; i < contacts.Length; i++) {
if (contacts[i] == null) {
contacts[i] = AddContact;
Console.WriteLine("Address Book updated. {0} has been added!", name);
break;
}
}
}
}
//changed - removed variable and all instances of...
private bool CheckEntry(string name) {
foreach(Contact contact in contacts) {
if (contact == null) {
break;
}
else if (contact != null && contact.Name != name) {
continue;
}
else if (contact.Name == name) {
return false;
}
}
return true;
}
//changed - instead of passing checkentry() as a check I just took care of it here
public void RemoveEntry(string name) {
for(int i = 0; i < contacts.Length; i++) {
if(contacts[i].Name == name) {
contacts[i] = null;
break;
}
}
Console.WriteLine("{0} removed from contacts", name);
}
In CheckEntry you are testing an object of type Contact with your param of type string. This cannot work as you are testing 2 different types.
It could work the way you wrote it if you override the ToString method in your Contact class (so that it gives the attribute contact.name).
You can modify your code like this (add using System.Linq):
private bool CheckEntry(string name)
{
return contacts.Any(a => a.Name == name);
}
First of, I am assuming your strategy is to find the first empty slot in the array and insert a new Contact in it for AddEntry. To remove an entry, you want to simply mark that array location as empty. As you realize, this means that the array does not grow dynamically with the request i.e. you can have an ArrayFull situation that you need to handle. Also, you are doing linear search a.k.a. scan of the array - I assume you don't want to focus on that aspect in this sample.
Below are my comments for your existing code:
Shouldn't AddEntry update the Address if Address is different even though the Name matches?
Also returning a bool to indicate if the address was added/updated (true) versus nothing was done (false)
You should also handle the 'ArrayFull` condition
I do not understand why you need the variable existingContact
Don't use functions named CheckXXX if you plan to return bool. ContainxXXX is better understood method name to use with a bool return value.
You should not break from your foreach loop in CheckEntry. What if 2 entries were added, then the first one removed and then request to add the second one comes again? You would just break out since first entry is null
You seem to have 3 different ifs to check in your foreach where only one suffices.
Below is my relevant code with comments. I tried to keep them similar to what you have. You can use LINQ in multiple places instead of looping.
class Contact {
public string Name { get; private set; } // use a property with a private setter, instead of a public member
public string Address { get; private set; } // use a property with a private setter, instead of a public member
public Contact(string name, string address) {
Name = name;
Address = address;
}
}
//------------------------------------------------------------------------
class AddressBook {
public readonly Contact[] contacts;
public AddressBook() {
contacts = new Contact[2]; // I am assuming you kept the size 2 for testing
}
public bool AddEntry(string name, string address) {
if (!ContainsEntry(name)) {
Contact AddContact = new Contact(name, address);
for (int i = 0; i < contacts.Length; i++) {
if (contacts[i] == null) {
contacts[i] = AddContact;
Console.WriteLine("Address Book updated. {0} has been added!", name);
return true;
}
}
Console.WriteLine($"Cannot add name ({name}) to Address Book since it is full!");
// TODO: Throw some exception or specific return values to indicate the same to the caller
} else {
Console.WriteLine($"Name ({name}) already exists in Address Book!");
// TODO: Update the address?
}
return false;
}
private int GetEntryIndex(string name) {
for (int i = 0; i < contacts.Length; i++) {
if (contacts[i] != null && contacts[i].Name == name)
return i;
}
return -1;
}
private bool ContainsEntry(string name) {
return GetEntryIndex(name) != -1;
}
public void RemoveEntry(string name) {
var index = GetEntryIndex(name);
if (index != -1) {
contacts[index] = null;
Console.WriteLine("{0} removed from contacts", name);
}
}
public string View() {
string contactList = "";
foreach (Contact contact in contacts) {
if (contact == null) {
continue; // Don't break, but simply continue to look further
}
contactList += String.Format("Name: {0} -- Address: {1}" + Environment.NewLine, contact.Name, contact.Address);
}
return contactList;
}
}
EDIT: Answering some of the questions the OP has
Q: What should I do with the return value of AddEntry
A: Right now, you are writing code for practice, but once you start writing industry standard code, you will soon realize that you wouldn't know who call's your function. Never assume (unless the method is private) that only you will call this function. Currently, you don't need to do anything with this return value, but time might come when you want to know if your call did indeed modify some values or just returned without changes. It is for that time. It is a pretty standard practice. Some folks even return an enum {NoChange, Added, Updated, Deleted} instead of a bool
Q: How to reduce the CheckEntry function.
A:
Below I have written your function with a slightly different formatting
private bool CheckEntry(string name) {
foreach (Contact contact in contacts) {
if (contact == null) { // First if
continue;
} else {
if (contact != null && contact.Name != name) { // Second if
continue;
} else {
if (contact.Name == name) { // Third if
return false;
}
}
}
}
return true;
}
For the first if statement, the else part is redudant. The second if statement will hit only when contact is not null due to the continue statement, cancelling the need for the else keyword.
Since contact is not null when we hit the second if statement, the check contact != null is pretty redundant in the second if. You can reduce the if statement's as below
if (contact.Name != name) { // Second if
continue;
} else {
if (contact.Name == name) { // Third if
return false;
}
}
Similarly you will notice that the the third if will hit only when contact.Name is the same as name (otherwise it would have continued). So there is no need to check again. This will reduce our checks as below
if (contact == null)
continue;
if (contact.Name != name)
continue;
else
return false;
This can be further reduced by combining the conditions in the two if statements as below
if (contact == null || contact.Name != name)
continue;
else
return false;
Which is the same as (negating the condition)
if (contact != null && contact.Name == name)
return false;
So your function will look like
private bool CheckEntry(string name) {
foreach (Contact contact in contacts)
if (contact != null && contact.Name == name)
return false;
return true;
}
Hope you follow the deductions here
Q: I may have misunderstood from the course but when you write the properties in a class with getters and setters, which I did change with my updated version, I was under the impression that it wasn't necessary, even redundant, to create a Constructor - that the class (not sure if this is correct terminology) has a default constructor built in for cases where you would want to add the property.
A: Correct. Just different ways of doing it.
Q: I like what you did with GetEntryIndex() and ContainsEntry() - I'm curious, is GetEntryIndex() not the same as writing your own Array.IndexOf(), though? Someone, either me or a method, has to scan the array. Both versions are linear so either way, this is O(n), correct? (just getting into some theory so please correct me if I'm wrong). So, just for my understanding, this is the same as: return Array.IndexOf(contacts, name); this returns -1 if it doesn't exist or whatever the index is(I'm assuming)
A: It is not the same, but similar in essence. IndexOf has a set of rules to figure out if given two objects are equal or not. You haven't defined these rules on your custom object, hence it will always return -1. There are simple LINQ statements to do these, but I would let you explore that on your own.
First, thank you for taking the time to make such a thorough response. Second, I should probably note that I am teaching myself/taking a course on Treehouse and I'm on my second week/second section of the course - just to give you an idea of where I'm coming from. That being said, I want to walk through what you have given me and where I was heading with this project so I can learn.
I agree the user should be able to update and it was a feature that I had considered but had not quite gotten there.
if returning a bool when added/updated would you then remove the update string and place it with the caller in main()? This way it only prints that it's been updated if return true else print that the contact was unchanged. It kind of feels like it might be advantageous to make a class that is suitable just checking values - which would also make my code more reusable?? If I was going to use it in Main() maybe something like this would work..
if(!addEntry()) {Console.WriteLine("Contact {0} was not updated", name);} //do something
else{Console.WriteLine("Address Book updated. {0} has been added!", name);}
Furthermore, if the user is just updating the contact it could print that it's been updated. So the Console output could be a ternary operation - if new name print contact added otherwise contact updated??
I agree and had run into the situation where nothing was done because the array was full and that was something I had planned on working on.
I absolutely agree about the existing contact variable. I was so stuck when it came to what to do that was the best I could come up with. I should have walked away for a bit and thought about it more. I don't know if you saw my updated portion, but I was able to eliminate it.
Contains for a bool method seems logical, I will be sure to follow this rule.
I had not considered this in such terms before you said that. It makes perfect sense though. If the first entry is null and the second contains the name that the user is trying to enter then you would end up with a duplicate since I was breaking from the loop at the first instance of null instead of making sure the name didn't exist in the entire array, first.
I'm not sure, without your methods, how I would have been able to eliminate my three conditionals. I see what you did to make the one work, but am I possibly missing something? Or, was that more of a reference saying, ok here's a better way - use these methods to check and then eliminate the if/else chain, thus making your code more concise.
for your code:
I may have misunderstood from the course but when you write the properties in a class with getters and setters, which I did change with my updated version, I was under the impression that it wasn't necessary, even redundant, to create a Constructor - that the class (not sure if this is correct terminology) has a default constructor built in for cases where you would want to add the property.
Correct, I did set the size to 2 for testing purposes.
I like what you did with GetEntryIndex() and ContainsEntry() - I'm curious, is GetEntryIndex() not the same as writing your own Array.IndexOf(), though? Someone, either me or a method, has to scan the array. Both versions are linear so either way, this is O(n), correct? (just getting into some theory so please correct me if I'm wrong). So, just for my understanding, this is the same as:
return Array.IndexOf(contacts, name);
this returns -1 if it doesn't exist or whatever the index is(I'm assuming)
That's a good idea with the continue in View(). That will make sure to print out any contact with an index higher than an index with a null value. By some magic, this was working in this way with the break(it would print out index 1 even if index 0 was empty), but I do realize why it shouldn't and how using continue is better.
I have a method inside a root entity (of type ProductOptionGroup) that sets properties on a reference to another object (target, also of type ProductOptionGroup).
I have to pass in by reference as the target variable is modified in the below method snippet:
void SetOptionDependency(Product sourceProduct, Product targetProduct, ref ProductOptionGroup target)
{
if (this.targetDependencyId == null)
{
this.targetDependencyId = target.Id;
}
if (this.targetDependencyId != target.Id)
{
// abort - reassignement of active dependency not allowed
}
if (this.map.Contains(sourceProduct.Id) == false)
{
// abort - the provided id is not associated with us
}
if (target.map.Contains(targetProduct.Id) == false)
{
// abort - the supplied id is not associated with the dependency target
}
// ** here the parameter passed in is modified **
target.associationCount[targetProduct.Id]++;
this.map.Add(sourceProduct.Id, targetProduct.Id);
}
I have put the code within the ProductOptionGroup aggregate as the business rules are most easily read there.
I guess the alternative could be to use a domain service to make the associations, but then some of the business logic and checking would come out of the entity and in to the service. I am not sure I like this idea.
The downside i see is that who ever is calling the method on the entity for would need to ensure that the entity is saved, along with the object modified by reference is also saved - the ref keyword on the method gives a big hint but is not explicit.
My question is does modifying variables passed in by reference to a entity method violate any DDD rules?
To give better context if needed, full code snippets below:
public class ProductOptionGroup
{
string Id; // our Id
string targetDependencyId; // we can link to one other ProductOptionsGroup by reference
MapList<string, string> map = new MapList<string, string>();
Dictionary<string, int> associationCount = new Dictionary<string, int>();
void AssociateProduct(Product product)
{
this.map.AddKey(product.Id);
}
void DisassociatedProduct(Product product)
{
if (this.map.Contains(product.Id) == false)
{
// abort - the provided id is not associated with us
}
// check children are not referring to this product
int count = 0;
if (this.associationCount.TryGetValue(product.Id, out count) && count > 0)
{
// abort - this product is being referenced
}
else
{
this.map.Remove(product.Id);
}
}
void SetOptionDependency(Product sourceProduct, Product targetProduct, ref ProductOptionGroup target)
{
if (this.targetDependencyId == null)
{
this.targetDependencyId = target.Id;
}
if (this.targetDependencyId != target.Id)
{
// abort - reassignement of active dependency not allowed
}
if (this.map.Contains(sourceProduct.Id) == false)
{
// abort - the provided id is not associated with us
}
if (target.map.Contains(targetProduct.Id) == false)
{
// abort - the supplied id is not associated with the dependency target
}
target.associationCount[targetProduct.Id]++;
this.map.Add(sourceProduct.Id, targetProduct.Id);
}
}
In short I have concluded modifying the state of one variable inside the function of another function is really bad news, I view this as a side effect.
As was mentioned in the comments, needing to modify multiple things at once pointed to deeper domain insight that was waiting to be explored to allow a more natural and clean model, and associated code.
https://softwareengineering.stackexchange.com/questions/40297/what-is-a-side-effect
I would like to prevent further processing on an object if it is null.
In the following code I check if the object is null by either:
if (!data.Equals(null))
and
if (data != null)
However, I receive a NullReferenceException at dataList.Add(data). If the object was null, it should never have even entered the if-statement!
Thus, I'm asking if this is proper way of checking if an object is null:
public List<Object> dataList;
public bool AddData(ref Object data)
bool success = false;
try
{
// I've also used "if (data != null)" which hasn't worked either
if (!data.Equals(null))
{
//NullReferenceException occurs here ...
dataList.Add(data);
success = doOtherStuff(data);
}
}
catch (Exception e)
{
throw new Exception(e.ToString());
}
return success;
}
If this is the proper way of checking if the object is null, what am I doing wrong (how can I prevent further processing on the object to avoid the NullReferenceException)?
It's not data that is null, but dataList.
You need to create one with
public List<Object> dataList = new List<Object>();
Even better: since it's a field, make it private. And if there's nothing preventing you, make it also readonly. Just good practice.
Aside
The correct way to check for nullity is if(data != null). This kind of check is ubiquitous for reference types; even Nullable<T> overrides the equality operator to be a more convenient way of expressing nullable.HasValue when checking for nullity.
If you do if(!data.Equals(null)) then you will get a NullReferenceException if data == null. Which is kind of comical since avoiding this exception was the goal in the first place.
You are also doing this:
catch (Exception e)
{
throw new Exception(e.ToString());
}
This is definitely not good. I can imagine that you put it there just so you can break into the debugger while still inside the method, in which case ignore this paragraph. Otherwise, don't catch exceptions for nothing. And if you do, rethrow them using just throw;.
in C# > 7 use if (obj is null)
For not null use:
C# 7-8: if (obj is object)
C# 9-: if (obj is not null)
These will ignore any == or != defined by the object (unless of course you want to use them for null checks)
For more, in the C# Language Reference, see is operator.
C# 6 has monadic null checking :)
before:
if (points != null) {
var next = points.FirstOrDefault();
if (next != null && next.X != null) return next.X;
}
return -1;
after:
var bestValue = points?.FirstOrDefault()?.X ?? -1;
Your dataList is null as it has not been instantiated, judging by the code you have posted.
Try:
public List<Object> dataList = new List<Object>();
public bool AddData(ref Object data)
bool success = false;
try
{
if (!data.Equals(null)) // I've also used if(data != null) which hasn't worked either
{
dataList.Add(data); //NullReferenceException occurs here
success = doOtherStuff(data);
}
}
catch (Exception e)
{
throw;
}
return success;
}
[Edited to reflect hint by #kelton52]
Simplest way is to do object.ReferenceEquals(null, data)
Since (null==data) is NOT guaranteed to work:
class Nully
{
public static bool operator ==(Nully n, object o)
{
Console.WriteLine("Comparing '" + n + "' with '" + o + "'");
return true;
}
public static bool operator !=(Nully n, object o) { return !(n==o); }
}
void Main()
{
var data = new Nully();
Console.WriteLine(null == data);
Console.WriteLine(object.ReferenceEquals(null, data));
}
Produces:
Comparing '' with 'Nully'
True
False
As of C# 9 you can do
if (obj is null) { ... }
For not null use
if (obj is not null) { ... }
If you need to override this behaviour use == and != accordingly.
No, you should be using !=. If data is actually null then your program will just crash with a NullReferenceException as a result of attempting to call the Equals method on null. Also realize that, if you specifically want to check for reference equality, you should use the Object.ReferenceEquals method as you never know how Equals has been implemented.
Your program is crashing because dataList is null as you never initialize it.
The problem in this case is not that data is null. It is that dataList itself is null.
In the place where you declare dataList you should create a new List object and assign it to the variable.
List<object> dataList = new List<object>();
With c#9 (2020) you can now check a parameter is null with this code:
if (name is null) { }
if (name is not null) { }
You can have more information here
As of C# 8 you can use the 'empty' property pattern (with pattern matching) to ensure an object is not null:
if (obj is { })
{
// 'obj' is not null here
}
This approach means "if the object references an instance of something" (i.e. it's not null).
You can think of this as the opposite of: if (obj is null).... which will return true when the object does not reference an instance of something.
For more info on patterns in C# 8.0 read here.
In addition to #Jose Ortega answer,
its better for use extension method
public static bool IsNull(this object T)
{
return T == null;
}
And use IsNull method for all of object like:
object foo = new object(); //or any object from any class
if (foo.IsNull())
{
// blah blah //
}
Jeffrey L Whitledge is right. Your `dataList´-Object itself is null.
There is also another problem with your code: You are using the ref-keyword, which means the argument data cannot be null! The MSDN says:
An argument passed to a ref parameter must first be initialized. This differs from out, whose arguments do not have to be explicitly initialized before they are passed
It's also not a good idea to use generics with the type `Object´. Generics should avoid boxing/unboxing and also ensure type safety. If you want a common type make your method generic. Finally your code should look like this:
public class Foo<T> where T : MyTypeOrInterface {
public List<T> dataList = new List<T>();
public bool AddData(ref T data) {
bool success = false;
try {
dataList.Add(data);
success = doOtherStuff(data);
} catch (Exception e) {
throw new Exception(e.ToString());
}
return success;
}
private bool doOtherStuff(T data) {
//...
}
}
As others have already pointed out, it's not data but rather likely dataList that is null. In addition to that...
catch-throw is an antipattern that almost always makes me want to throw up every time that I see it. Imagine that something goes wrong deep in something that doOtherStuff() calls. All you get back is an Exception object, thrown at the throw in AddData(). No stack trace, no call information, no state, nothing at all to indicate the real source of the problem, unless you go in and switch your debugger to break on exception thrown rather than exception unhandled. If you are catching an exception and just re-throwing it in any way, particularly if the code in the try block is in any way nontrivial, do yourself (and your colleagues, present and future) a favor and throw out the entire try-catch block. Granted, throw; is better than the alternatives, but you are still giving yourself (or whoever else is trying to fix a bug in the code) completely unnecessary headaches. This is not to say that try-catch-throw is necessarily evil per se, as long as you do something relevant with the exception object that was thrown inside the catch block.
Then there's the potential problems of catching Exception in the first place, but that's another matter, particularly since in this particular case you throw an exception.
Another thing that strikes me as more than a little dangerous is that data could potentially change value during the execution of the function, since you are passing by reference. So the null check might pass but before the code gets to doing anything with the value, it's changed - perhaps to null. I'm not positive if this is a concern or not (it might not be), but it seems worth watching out for.
public static bool isnull(object T)
{
return T == null ? true : false;
}
use:
isnull(object.check.it)
Conditional use:
isnull(object.check.it) ? DoWhenItsTrue : DoWhenItsFalse;
Update (another way) updated 08/31/2017 and 01/25/2021. Thanks for the comment.
public static bool IsNull(object T)
{
return (bool)T ? true : false;
}
Demostration
And for the records, you have my code on Github, go check it out:
https://github.com/j0rt3g4/ValidateNull
PS: This one is especially for you Chayim Friedman, don't use beta software assuming that is all true. Wait for final versions or use your own environment to test, before assuming true beta software without any sort of documentation or demonstration from your end.
Whenever you are creating objects of class you have to check the whether the object is null or not using the below code.
Example:
object1 is object of class
void myFunction(object1)
{
if(object1!=null)
{
object1.value1 //If we miss the null check then here we get the Null Reference exception
}
}
There is a one-liner in .NET 6
ExampleMethod(null);
void ExampleMethod(object param)
{
ArgumentNullException.ThrowIfNull(param);
// Do something
}
I just followed a method that we would usually follow in java script. To convert object to string and then check whether they are null.
var obj = new Object();
var objStr = obj.ToString();
if (!string.IsNullOrEmpty(objStr)){
// code as per your needs
}
I did more simple (positive way) and it seems to work well.
Since any kind of "object" is at least an object
if (MyObj is Object)
{
//Do something .... for example:
if (MyObj is Button)
MyObj.Enabled = true;
}
You can try like below
public List<Object> dataList;
public bool AddData(ref Object data)
bool success = false;
try
{
if (data != null)
{
dataList.Add(data);
success = doOtherStuff(data);
}
}
catch (Exception e)
{
throw new Exception(e.ToString());
}
return success;
}
Here are some extensions I use:
/// <summary>
/// Extensions to the object class
/// </summary>
public static class ObjectExtensions
{
/// <summary>
/// True if the object is null, else false
/// </summary>
public static bool IsNull(this object input) => input is null;
/// <summary>
/// False if the object is null, else true
/// </summary>
public static bool NotNull(this object input) => !IsNull(input);
}
public bool IsVisible(object ClaimCount)
{
bool flag = true;
#region || HIDE COLUMNS ON CONDITION BASIS
if (!String.IsNullOrEmpty(Convert.ToString(ClaimCount)))
{
Int32 ClaimCnt = Convert.ToInt32(ClaimCount);
if (ClaimCnt == 1)
{
flag = false;
}
}
#endregion
return flag;
}