En C ++, vous pouvez écrire le code suivant:
averageAttribute(animals, &Animal::age);
Existe-t-il des fonctionnalités similaires en C #?
Edit: Pour clarifier, Je ne demande pas des pointeurs. Je demande des "pointeurs vers les membres", une fonctionnalité trouvée en C ++ qui est utilisée avec les opérateurs . * et -> * .
Voici un exemple de cas d'utilisation pour les membres vers des pointeurs.
Disons que nous avons la classe suivante:
int averageAttribute(Animal[] animals, Animal::* member)
{
double average = 0;
for (â¦)
average += animals[i].*member; //(animals[i] + [offset])*
return average/animals.length;
}
3 Réponses :
non, c # n'a pas de fonctionnalité pour pointer vers les membres d'un objet (de référence) comme le fait c ++.
mais pourquoi? Un pointeur est considéré comme dangereux. Et même dans une zone non sécurisée, vous ne pouvez pas pointer vers une référence ou une structure qui contient des références, car une référence d'objet peut être récupérée même si un pointeur pointe vers elle. Le ramasse-miettes ne garde pas trace de savoir si un objet est pointé par des types de pointeurs.
vous avez mentionné que beaucoup de code dupliqué est utilisé pour l'implémenter sans pointeur, ce qui n'est pas vrai.
La vitesse dépend de la façon dont le JIT le compile, mais vous n'avez pas testé?
Si vous pensez que la quantité de commentaires sous votre Q montre que vous n'avez pas vraiment touché un inconvénient communément accepté de c #
struct CoOrds
{
public int x;
public int y;
}
class AccessMembers
{
static void Main()
{
CoOrds home;
unsafe
{
CoOrds* p = &home;
p->x = 25;
p->y = 12;
System.Console.WriteLine("The coordinates are: x={0}, y={1}", p->x, p->y );
}
}
}
le unsafe zone de c # sert quelques façons d'accéder aux membres par pointeur, mais uniquement aux types de valeur comme les structures simples et non pour un tableau de structures.
var Animals = new Animal[100];
//fill array
var AvgAnimal = new Animal() {
age = (int)Animals.Average(a => a.age ),
height = (int)Animals.Average(a => a.height),
weight = (int)Animals.Average(a => a.weight)
};
Où ai-je dit que beaucoup de code en double est utilisé pour l'implémenter à la manière C #? En outre, j'ai répertorié la solution que vous avez décrite ci-dessus, affirmant que elle était plus lente que les pointeurs vers les membres car elle nécessite des appels de fonction entiers. De plus, je suppose qu'un appel de fonction et un accès membre sont plus lents qu'un accès membre défini par l'exécution sans voir les résultats des tests, mais si vous souhaitez affirmer que ce n'est pas le cas, je suis tout à fait à l'écoute.
@NetherGranite vérifiez votre Q: "Nous finirions par copier et coller beaucoup de code ici, et si notre algorithme pour trouver la moyenne changeait, nous serions confrontés à un cauchemar de maintenance"
Vous l'avez peut-être mal lu. Cette partie de l'exemple n'avait rien à voir avec C #; cela s'appliquerait à n'importe quelle langue. J'ai ensuite proposé d'utiliser des délégués comme vous l'avez fait ci-dessus, ce que vous devriez faire en C # (et dans quelques autres langues comme Java).
Oh, et je devrais ajouter que les commentaires sous la question montrent en fait que les pointeurs vers les membres ne sont pas quelque chose dont les développeurs C # sont même conscients. Clairement, ils montrent que ce n'est pas un inconvénient communément accepté de C # comme vous l'avez dit, mais la seule raison pour laquelle cela m'a été présenté jusqu'à présent est que les développeurs C # ne sont même pas conscients qu'il existe des alternatives. Il y en a probablement qui peuvent dire pourquoi C # n'a pas de pointeurs vers les membres, et j'aimerais les entendre.
ok, certaines raisons pour lesquelles les pointeurs vers les membres ne sont pas implémentés ou peuvent être considérés comme dangereux pourraient aider. mais tu n'as pas demandé ça ... maintenant on dirait que tu essaies de troller ...
Vous avez raison; Je n'ai pas demandé cela. Cependant, la discussion que moi et d'autres avons eue à propos de ce poste a évolué vers cela. Ma question initiale est toujours d'actualité, et personne n'a encore donné de réponse faisant autorité qui ne soit pas simplement une suggestion sur la façon d'obtenir la fonctionnalité avec un autre mécanisme C #. Que voulez-vous dire quand vous dites que j'aime me promener?
Vous pouvez obtenir le même comportement en utilisant des délégués mais ce n'est pas la même chose que les délégués sont des pointeurs vers des fonctions en C ++. Ce que vous essayez de réaliser est possible en C # mais pas comme vous le faites en C ++.
Je pense à une solution utilisant Func:
Animal.AverageAttributeDelegates2(animals, x => x.Height).Dump(); //1.5 Animal.AverageAttributeDelegates2(animals, x => x.Weight).Dump(); //3 Animal.AverageAttributeDelegates2(animals, x => x.Name).Dump(); //0
Pardonnez-moi si c'est évident, mais en quoi est-ce différent de la solution que j'ai décrite? D'après ce que je peux voir, ils sont identiques. Aussi, quel est l’avantage d’avoir le getter renvoyer des objets s au lieu de int s?
L'objet @NetherGranite vous permet d'utiliser n'importe quel type de propriétés, avec int, vous ne pouvez passer à la méthode que des propriétés int
Oh je vois. Oui, cela serait certainement utile dans certains cas.
Comme d'autres personnes l'ont commenté ici, les délégués sont le moyen d'y parvenir en C #.
Même si un délégué peut obtenir la même fonctionnalité, c'est plutôt moyen inefficace d'obtenir un membre d'une structure si vous savez que vous ne le faites pas ont réellement besoin de la liberté que vous offre une fonction
Cela dépend de la manière dont le compilateur et le runtime implémentent ce délégué. Ils pourraient très bien voir qu'il s'agit d'une fonction triviale et optimiser l'appel, comme ils le font pour les getters et les setters triviaux. En F # par exemple, vous pouvez réaliser ceci:
type Animal = { Age : int }
let getAge (animal:Animal) =
animal.Age
let inline average (prop:Animal->int) (animals:Animal[]) =
let mutable avg = 0.
for animal in animals do
avg <- avg + float(prop(animal)) // no function call in the assembly here when calling averageAge
avg / (float(animals.Length))
let averageAge = average getAge
Merci! Vous avez non seulement répondu clairement à la question, mais vous avez également abordé chaque point de la discussion qui a résulté de la question, et puis en plus de cela, vous avez énuméré une alternative avec un exemple. Il est très intéressant que F # vous permette de demander explicitement l'inlining.
inline vous donne également une partie de la puissance des modèles C ++, car vous pouvez les rendre génériques tout en accédant directement aux membres d'objet (pas via une interface) avec des contraintes de membre . Ainsi, vous pouvez ajouter deux choses qui prennent en charge 'op_Addition' par exemple, ce qui est impossible sans passer par une interface ou un délégué en C #.
bien sûr, des références. Jetez un œil à
ref-keyword. Cependant, vous n'en aurez pas besoin pour définir la propriété d'unAnimal, utilisez simplementa.Age = 50.Lorsque vous voyez quelque chose faisant référence à un type de référence (un type déclaré avec class au lieu de struct), alors vous traitez avec l'objet via un pointeur. En C ++, tout est un type valeur par défaut. En C #, tout est un type de référence par défaut. a.Age accède déjà à la propriété Age via une référence à une instance d'Animal
@HimBromBeere C'était juste un exemple de code pour illustrer ce que je voulais dire par "pointeur vers un membre".
Au lieu de décrire ce dont vous pensez avoir besoin, décrivez ce que vous voulez faire (quel est votre problème réel et où vous êtes-vous coincé, en pensant que vous avez besoin de quelque chose comme des pointeurs vers des membres en c ++).
Vous pouvez avoir un délégué, qui est essentiellement une référence à une méthode. C'est le plus proche auquel je puisse penser pour le moment. stackoverflow.com/questions/724143/... . Je ne vois pas l'intérêt en C # d'avoir la fonctionnalité C ++ que vous décrivez cependant. Je serais intéressé par une explication des avantages d'une telle fonctionnalité, si cela ne va pas trop loin du sujet principal
Utile en C ++ ne le rend pas utile en C #. Pourquoi les trouvez-vous utiles? Quel objectif spécifique essayez-vous d'atteindre?
@HimBromBeere Pourquoi ne serait-il pas utile en C #? Parce qu'il y a d'autres fonctionnalités de C # qui sont meilleures que les membres vers des pointeurs? Si oui, quelles sont ces fonctionnalités? Je n'ai pas de problème spécifique que j'essaie de résoudre pour le moment, mais je sais à quel point les pointeurs vers les membres peuvent être utiles en C ++, et je m'imagine vouloir la même fonctionnalité en C #. Il pourrait également être excessif qu'il y ait une question pour chaque cas d'utilisation pour les membres vers des pointeurs et comment les atteindre en C #. Veuillez me corriger si vous n'êtes pas d'accord.
Selon ce que vous faites réellement, la fonction équivalente peut être complètement différente. C'est pourquoi nous devons savoir ce que précisément vous essayez d'accomplir. Vous avez déjà des fonctionnalités qui font des choses similaires dans les commentaires. Cependant - comme je l'ai déjà dit - il n'y a pas de relation 1: 1 entre les caractéristiques du langage. Vous devrez peut-être restructurer un peu votre code en fonction du cas d'utilisation.
@HimBromBeere D'accord, j'ai ajouté un cas d'utilisation. Est-ce assez complet pour que l'on puisse commenter d'éventuels parallèles en C #?
Eh bien, pour le premier exemple, vous pouvez utiliser des délégués comme suggéré dans les commentaires. Le deuxième est quelque chose que je ne sais pas, car j'ai rarement besoin de savoir combien d'octets sont consommés par une instance (rappelez-vous: dans un langage géré, GC libère la mémoire pour vous assez décemment).
@HimBromBeere Pardonnez-moi si c'est une question stupide, mais qu'entendez-vous par «premier» et «deuxième» exemple?
Celui avec les moyennes. En C #, vous pouvez faire ceci:
Moyenne (animaux, x => x.Age)ouMoyenne (animaux, x => x.Height). Le second est la taille du point. Ai-je oublié quelque chose?@HimBromBeere Oh duh, j'ai oublié que j'avais écrit un exemple quand j'ai posé ma question pour la première fois. Oui, vous pouvez utiliser des délégués comme je l'ai dit; cependant, un foncteur entier pour récupérer la valeur du membre d'une structure est lent comme je l'ai dit. Les délégués sont-ils la seule option en C #?
Le seul que je connaisse. Je ne sais pas si ces "foncteurs" sont vraiment aussi lents ou si cela compte vraiment beaucoup. Habituellement, vous rencontrez d'autres problèmes qu'une nano-seconde à partir d'un tel accès aux membres.
@HimBromBeere Désolé, j'ai mal lu ce que vous avez dit. Oui, vous avez peut-être manqué quelque chose. J'ai supprimé la chose
sizeofcar c'est probablement une distraction. Les délégués ne sont-ils pas des foncteurs - des classes avec une seule fonction générée par le compilateur? Mon argument était qu'une fonction entière (ce qui est ce qu'un délégué est pour autant que je sache) est inutile lorsque tout ce qui est nécessaire est un simple pointeur mathématique - math qui peut être réalisé via des pointeurs vers des membres en C ++.@NetherGranite: Si vous pensez trier et comparer des choses, des choses sont faites au niveau de la compilation JIT pour optimiser les appels de méthode. Voir blogs. msdn.microsoft.com/dotnet/2018/04/18/... où EqualityComparer .Default.Equals (items [i], -1)) pour comparer deux entiers n'est pas compilé en un appel de méthode inutile mais à certains mov, cmp et un appel de saut. Il n'y a pas de membres pointeurs en C # comme celui-ci, mais si vous connaissez les décalages, vous pouvez réaliser des choses similaires avec des pointeurs et du code non sécurisé si vous en avez besoin.
@AloisKraus Je me suis demandé si le compilateur était assez intelligent pour évacuer certains appels de foncteurs, c'est intéressant. Je me demande si cela s'applique à tous les délégués qui sont juste
(animal) => animal.ageou quelque chose de similaire. Le problème avec les pointeurs vers les membres est que le compilateur les transforme en décalages pour vous; ils sont les décalages.animal. * p_agese traduirait par quelque chose comme(animal + [offset here]) *.@HimBromBeere J'ai remplacé toutes les mentions de "foncteur" par "délégué" et "fonction" pour utiliser les termes C #. Je n'ai peut-être même pas utilisé correctement "fonctor" en premier lieu. J'espère que cela a plus de sens maintenant.
@NetherGranite De votre point de vue, le nettoyage de la mémoire peut être inutile lorsque vous pouvez désallouer manuellement la mémoire en C ++. Je ne comprends pas le but de cette discussion ici. Si vous voulez en savoir plus sur le fonctionnement du CLR, du compilateur JIT et sur le fonctionnement d'autres éléments, vous pouvez toujours lire la documentation pour le découvrir.
Si je comprends bien, c'est un pointeur vers un membre quelconque de la classe (Animal dans votre exemple)?
@SeM Pour être honnête, je ne sais pas comment ce que vous avez dit était lié à ce qui a été dit. D'où vient le ramassage des ordures? En ce qui concerne le compilateur, Alois a mentionné une optimisation qui résout les simples délégués, ce qui est très pertinent pour cette question car la fonctionnalité des membres vers les pointeurs peut être réalisée avec de simples délégués mais les délégués sont des appels de fonction entiers et donc beaucoup plus lents.
@ Phate01 Tout comme les "tableaux" sont des blocs de mémoire contigus, les structs le sont aussi. Supposons que vous connaissiez l'adresse d'un
Animalstocké dans la variableanimal. Si vous saviez que sonâgese situait exactement 6 octets avant l'adresseanimal, alors vous pourriez théoriquement dire quelque chose comme(animal + 6) * code > pour avoir l'âge. C'est le même concept que les tableaux; si vous connaissez l'adresse du premier élément d'un "tableau" deAnimals stockés dans la variablearr, si vous savez que le troisième élément se trouve à 3 octets du premier élément, vous pouvez dire(arr + 3) *. (Notez que le calcul du pointeur est évidemment faux.)Funcetdelegatesont les solutions courantes ici. Ils peuvent ou non être aussi performants que C ++ - mais ils constituent le moyen standard de résoudre le problème.@NetherGranite: Aucun appel de délégué ne disparaîtra car c'est quelque chose qui permet de changer la destination de l'appel lors de l'exécution. Le compilateur ne pouvait optimiser que ceux qui ne changent jamais. Mais dans ce cas, vous utiliseriez simplement un appel de méthode de toute façon.
quelqu'un a-t-il réellement comparé la vitesse d'exécution? Je doute que l'approche hacky c ++ soit un avantage du tout
Découvrez les fonctions
inlineen F #. Il est possible que le compilateur optimise complètement la fonction, se retrouvant simplement avec une arithmétique de pointeur dans le code d'assemblage. Je n'ai pas vérifié, mais cela devrait être faisable.@FalcoAlexander Qu'est-ce qui l'a rendu piraté? Le fait que vous ne le reconnaissez pas? Aucun développeur de logiciels familier avec les pointeurs vers les membres ne regarderait ce code et penserait qu'il est "piraté"; cette utilisation est le point même des pointeurs vers les membres. Pouvez-vous clarifier? Si quoi que ce soit, écrire une classe entière, puis instancier un objet, puis passer cet objet à la fonction moyenne (c'est-à-dire utiliser des délégués ou des objets fonction en général pour résoudre ce problème spécifique) est un piratage.
@NetherGranite, il semble que vous n'êtes pas vraiment intéressé par une solution, mais que ce sont des mots qui divisent et que vous aimez évidemment plus discuter que développer. Je trouverais utile de comparer certains points de repère qui montrent l'avantage de l'accès au pointeur, mais je n'ai pas installé de charge de travail C ++. dans 99% je suis content de ne pas avoir besoin de traiter des pointeurs comme quand je codais dans 68k assembleur il y a 35 ans
@FalcoAlexander Je suis intéressé par une réponse à cette question. La réponse à cette question semble être "les délégués sont le le moyen de réaliser cette fonctionnalité", mais personne ne l’a dit avec autorité; les gens ont seulement dit que les délégués sont un moyen de réaliser cette fonctionnalité, laissant la possibilité d’autres solutions. Aussi, comment est-ce que je partage les mots? Parlez-vous de "hacky"? Je vous ai demandé ce que vous vouliez dire par "hacky". Vous vouliez clairement dire quelque chose par là, et je me demandais quoi, mais il me semblait que vous l'appeliez "hacky" simplement parce que vous ne l'aviez pas vu auparavant.
@NetherGranite J'ai modifié ma réponse pour ajouter du code c # dangereux que vous pourriez rechercher?! si c'est vraiment ce que vous cherchiez, j'ai simplement googlé "c # member pointers"
@FalcoAlexander Non, ce n'était pas ce que je cherchais. Cela n'est pas lié à la question.